diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3ccd98..d42409f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,10 @@ on: defaults: run: shell: bash + +permissions: + id-token: write + jobs: build: @@ -37,6 +41,19 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress --no-suggest + + - name: Setup OIDC + run: npm install @actions/core@1.6.0 @actions/http-client + + - name: Get Id Token + uses: actions/github-script@v7 + id: idtoken + with: + script: | + const coreDemo = require('@actions/core'); + const idToken = await coreDemo.getIDToken('sts.aliyuncs.com'); + const fsx = require('fs/promises'); + await fsx.writeFile('/tmp/oidc_token', idToken); - name: Run test case run: composer test @@ -46,7 +63,13 @@ jobs: ROLE_ARN: ${{ secrets.ROLE_ARN }} PUBLIC_KEY_ID: ${{ secrets.PUBLIC_KEY_ID }} PRIVATE_KEY_LINE_1: ${{ secrets.PRIVATE_KEY_LINE_1 }} + + # for OIDC + ALIBABA_CLOUD_OIDC_PROVIDER_ARN: ${{ secrets.ALIBABA_CLOUD_OIDC_PROVIDER_ARN }} + ALIBABA_CLOUD_OIDC_TOKEN_FILE: "/tmp/oidc_token" + ALIBABA_CLOUD_ROLE_ARN: ${{ secrets.OIDC_ROLE_ARN }} + - name: Upload Coverage Report uses: codecov/codecov-action@v4 with: - token: ${{ secrets.CODECOV_TOKEN }} # required \ No newline at end of file + token: ${{ secrets.CODECOV_TOKEN }} # required diff --git a/README-zh-CN.md b/README-zh-CN.md index 8efd982..a0ffe53 100644 --- a/README-zh-CN.md +++ b/README-zh-CN.md @@ -134,7 +134,6 @@ $bearerToken = new Credential([ 'bearer_token' => '', ]); $bearerToken->getBearerToken(); -$bearerToken->getSignature(); ``` ## 默认凭证提供程序链 diff --git a/README.md b/README.md index 8a8f6e5..d9f9751 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,6 @@ $bearerToken = new Credential([ 'bearer_token' => '', ]); $bearerToken->getBearerToken(); -$bearerToken->getSignature(); ``` ## Default credential provider chain diff --git a/phpunit.xml b/phpunit.xml index 6b14449..4b94cb7 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -39,15 +39,11 @@ ./src - ./src/Profile/DefaultProfile.php - ./src/DefaultAcsClient.php - ./src/Release.php - ./src/SDK.php - ./src/Functions.php - ./src/Constants/Business.php - ./src/Constants/ErrorCode.php - ./src/Signature/Signature.php - ./src/Credentials/CredentialsInterface.php + ./src/Credential/Config.php + ./src/Credential/CredentialModel.php + ./src/Providers/CredentialsProvider.php + ./src/CredentialsInterface.php + ./src/CredentialsProviderWrap.php diff --git a/src/AccessKeyCredential.php b/src/AccessKeyCredential.php index 6d7d7c9..05a0132 100644 --- a/src/AccessKeyCredential.php +++ b/src/AccessKeyCredential.php @@ -2,9 +2,12 @@ namespace AlibabaCloud\Credentials; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; /** + * @deprecated * Use the AccessKey to complete the authentication. */ class AccessKeyCredential implements CredentialsInterface @@ -29,7 +32,7 @@ public function __construct($access_key_id, $access_key_secret) { Filter::accessKey($access_key_id, $access_key_secret); - $this->accessKeyId = $access_key_id; + $this->accessKeyId = $access_key_id; $this->accessKeySecret = $access_key_secret; } @@ -69,4 +72,15 @@ public function getSecurityToken() { return ''; } + /** + * @inheritDoc + */ + public function getCredential() + { + return new CredentialModel([ + 'accessKeyId' => $this->accessKeyId, + 'accessKeySecret' => $this->accessKeySecret, + 'type' => 'access_key', + ]); + } } diff --git a/src/BearerTokenCredential.php b/src/BearerTokenCredential.php index c38fb8c..34fab6f 100644 --- a/src/BearerTokenCredential.php +++ b/src/BearerTokenCredential.php @@ -2,6 +2,8 @@ namespace AlibabaCloud\Credentials; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\BearerTokenSignature; /** @@ -50,4 +52,16 @@ public function getSignature() { return new BearerTokenSignature(); } + + /** + * @inheritDoc + */ + public function getCredential() + { + return new CredentialModel([ + 'bearerToken' => $this->bearerToken, + 'type' => 'bearer', + ]); + } + } diff --git a/src/Credential.php b/src/Credential.php index 3916c1d..fb89018 100644 --- a/src/Credential.php +++ b/src/Credential.php @@ -3,172 +3,257 @@ namespace AlibabaCloud\Credentials; use AlibabaCloud\Credentials\Credential\Config; +use AlibabaCloud\Credentials\Credential\CredentialModel; +use AlibabaCloud\Credentials\Providers\DefaultCredentialsProvider; +use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider; +use AlibabaCloud\Credentials\Providers\OIDCRoleArnCredentialsProvider; +use AlibabaCloud\Credentials\Providers\RamRoleArnCredentialsProvider; +use AlibabaCloud\Credentials\Providers\RsaKeyPairCredentialsProvider; +use AlibabaCloud\Credentials\Providers\StaticAKCredentialsProvider; +use AlibabaCloud\Credentials\Providers\StaticSTSCredentialsProvider; +use AlibabaCloud\Credentials\Providers\URLCredentialsProvider; +use AlibabaCloud\Credentials\Utils\Helper; +use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; -use ReflectionClass; -use ReflectionException; -use ReflectionParameter; +use RuntimeException; /** * Class Credential * * @package AlibabaCloud\Credentials * - * @mixin AccessKeyCredential - * @mixin BearerTokenCredential - * @mixin EcsRamRoleCredential - * @mixin RamRoleArnCredential - * @mixin RsaKeyPairCredential */ class Credential { - /** - * @var array - */ - protected $config = []; /** - * @var array + * Version of the Client */ - protected $types = [ - 'access_key' => AccessKeyCredential::class, - 'sts' => StsCredential::class, - 'ecs_ram_role' => EcsRamRoleCredential::class, - 'ram_role_arn' => RamRoleArnCredential::class, - 'rsa_key_pair' => RsaKeyPairCredential::class, - 'bearer' => BearerTokenCredential::class, - ]; + const VERSION = '1.1.5'; /** - * @var AccessKeyCredential|BearerTokenCredential|EcsRamRoleCredential|RamRoleArnCredential|RsaKeyPairCredential + * @var Config */ - protected $credential; + protected $config; /** - * @var string + * @var CredentialsInterface */ - protected $type; + protected $credential; /** * Credential constructor. * * @param array|Config $config - * - * @throws ReflectionException */ public function __construct($config = []) { - if ($config instanceof Config) { - $config = $this->parse($config); - } - if ($config !== []) { - $this->config = array_change_key_case($config); - $this->parseConfig(); + if (\is_array($config)) { + if (empty($config)) { + $this->config = null; + } else { + $this->config = new Config($this->parseConfig($config)); + } } else { - $this->credential = Credentials::get()->getCredential(); + $this->config = $config; } + $this->credential = $this->getCredentials($this->config); } /** - * @param Config $config + * @param array $config * * @return array */ - private function parse($config) + private function parseConfig($config) { - $config = get_object_vars($config); - $res = []; - foreach ($config as $key => $value) { - $res[$this->toUnderScore($key)] = $value; + $res = []; + foreach (\array_change_key_case($config) as $key => $value) { + $res[Helper::snakeToCamelCase($key)] = $value; } return $res; } - private function toUnderScore($str) - { - $dstr = preg_replace_callback('/([A-Z]+)/', function ($matchs) { - return '_' . strtolower($matchs[0]); - }, $str); - return trim(preg_replace('/_{2,}/', '_', $dstr), '_'); - } + /** - * @throws ReflectionException + * Credentials getter. + * + * @param Config $config + * @return CredentialsInterface + * */ - private function parseConfig() + private function getCredentials($config) { - if (!isset($this->config['type'])) { - throw new InvalidArgumentException('Missing required type option'); + if (is_null($config)) { + return new CredentialsProviderWrap('default', new DefaultCredentialsProvider()); } - - $this->type = $this->config['type']; - if (!isset($this->types[$this->type])) { - throw new InvalidArgumentException( - 'Invalid type option, support: ' . - implode(', ', array_keys($this->types)) - ); + switch ($config->type) { + case 'access_key': + $provider = new StaticAKCredentialsProvider([ + 'accessKeyId' => $config->accessKeyId, + 'accessKeySecret' => $config->accessKeySecret, + ]); + return new CredentialsProviderWrap('access_key', $provider); + case 'sts': + $provider = new StaticSTSCredentialsProvider([ + 'accessKeyId' => $config->accessKeyId, + 'accessKeySecret' => $config->accessKeySecret, + 'securityToken' => $config->securityToken, + ]); + return new CredentialsProviderWrap('sts', $provider); + case 'bearer': + return new BearerTokenCredential($config->bearerToken); + case 'ram_role_arn': + if (!is_null($config->securityToken) && $config->securityToken !== '') { + $innerProvider = new StaticSTSCredentialsProvider([ + 'accessKeyId' => $config->accessKeyId, + 'accessKeySecret' => $config->accessKeySecret, + 'securityToken' => $config->securityToken, + ]); + } else { + $innerProvider = new StaticAKCredentialsProvider([ + 'accessKeyId' => $config->accessKeyId, + 'accessKeySecret' => $config->accessKeySecret, + ]); + } + $provider = new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $innerProvider, + 'roleArn' => $config->roleArn, + 'roleSessionName' => $config->roleSessionName, + 'policy' => $config->policy, + 'durationSeconds' => $config->roleSessionExpiration, + 'externalId' => $config->externalId, + 'stsEndpoint' => $config->STSEndpoint, + ], [ + 'connectTimeout' => $config->connectTimeout, + 'readTimeout' => $config->readTimeout, + ]); + return new CredentialsProviderWrap('ram_role_arn', $provider); + case 'rsa_key_pair': + $provider = new RsaKeyPairCredentialsProvider([ + 'publicKeyId' => $config->publicKeyId, + 'privateKeyFile' => $config->privateKeyFile, + 'durationSeconds' => $config->roleSessionExpiration, + 'stsEndpoint' => $config->STSEndpoint, + ], [ + 'connectTimeout' => $config->connectTimeout, + 'readTimeout' => $config->readTimeout, + ]); + return new CredentialsProviderWrap('rsa_key_pair', $provider); + case 'ecs_ram_role': + $provider = new EcsRamRoleCredentialsProvider([ + 'roleName' => $config->roleName, + 'disableIMDSv1' => $config->disableIMDSv1, + ], [ + 'connectTimeout' => $config->connectTimeout, + 'readTimeout' => $config->readTimeout, + ]); + return new CredentialsProviderWrap('ecs_ram_role', $provider); + case 'oidc_role_arn': + $provider = new OIDCRoleArnCredentialsProvider([ + 'roleArn' => $config->roleArn, + 'oidcProviderArn' => $config->oidcProviderArn, + 'oidcTokenFilePath' => $config->oidcTokenFilePath, + 'roleSessionName' => $config->roleSessionName, + 'policy' => $config->policy, + 'durationSeconds' => $config->roleSessionExpiration, + 'stsEndpoint' => $config->STSEndpoint, + ], [ + 'connectTimeout' => $config->connectTimeout, + 'readTimeout' => $config->readTimeout, + ]); + return new CredentialsProviderWrap('oidc_role_arn', $provider); + case "credentials_uri": + $provider = new URLCredentialsProvider([ + 'credentialsURI' => $config->credentialsURI, + ], [ + 'connectTimeout' => $config->connectTimeout, + 'readTimeout' => $config->readTimeout, + ]); + return new CredentialsProviderWrap('credentials_uri', $provider); + default: + throw new InvalidArgumentException('Unsupported credential type option: ' . $config->type . ', support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri'); } + } - $class = new ReflectionClass($this->types[$this->type]); - $parameters = []; - /** - * @var $parameter ReflectionParameter - */ - foreach ($class->getConstructor()->getParameters() as $parameter) { - $parameters[] = $this->getValue($parameter); - } + /** + * @return CredentialModel + * @throws RuntimeException + * @throws GuzzleException + */ + public function getCredential() + { + return $this->credential->getCredential(); + } - $this->credential = $class->newInstance(...$parameters); + /** + * @return array + */ + public function getConfig() + { + return $this->config->toMap(); } /** - * @param ReflectionParameter $parameter + * @deprecated use getCredential() instead * - * @return string|array - * @throws ReflectionException + * @return string + * @throws RuntimeException + * @throws GuzzleException */ - protected function getValue(ReflectionParameter $parameter) + public function getType() { - if ($parameter->name === 'config' || $parameter->name === 'credential') { - return $this->config; - } - - foreach ($this->config as $key => $value) { - if (strtolower($parameter->name) === $key) { - return $value; - } - } - - if ($parameter->isDefaultValueAvailable()) { - return $parameter->getDefaultValue(); - } - - throw new InvalidArgumentException("Missing required {$parameter->name} option in config for {$this->type}"); + return $this->credential->getCredential()->getType(); } /** - * @return AccessKeyCredential|BearerTokenCredential|EcsRamRoleCredential|RamRoleArnCredential|RsaKeyPairCredential + * @deprecated use getCredential() instead + * + * @return string + * @throws RuntimeException + * @throws GuzzleException */ - public function getCredential() + public function getAccessKeyId() { - return $this->credential; + return $this->credential->getCredential()->getAccessKeyId(); } /** - * @return array + * @deprecated use getCredential() instead + * + * @return string + * @throws RuntimeException + * @throws GuzzleException */ - public function getConfig() + public function getAccessKeySecret() { - return $this->config; + return $this->credential->getCredential()->getAccessKeySecret(); } /** + * @deprecated use getCredential() instead + * * @return string + * @throws RuntimeException + * @throws GuzzleException */ - public function getType() + public function getSecurityToken() { - return $this->type; + return $this->credential->getCredential()->getSecurityToken(); } + /** + * @deprecated use getCredential() instead + * + * @return string + * @throws RuntimeException + * @throws GuzzleException + */ + public function getBearerToken() + { + return $this->credential->getCredential()->getBearerToken(); + } /** * @param string $name diff --git a/src/Credential/Config.php b/src/Credential/Config.php index e83b0a2..1fb57e3 100644 --- a/src/Credential/Config.php +++ b/src/Credential/Config.php @@ -2,53 +2,269 @@ namespace AlibabaCloud\Credentials\Credential; -class Config +use AlibabaCloud\Tea\Model; + +class Config extends Model { + public function validate() + { + } + public function toMap() + { + $res = []; + if (null !== $this->accessKeyId) { + $res['accessKeyId'] = $this->accessKeyId; + } + if (null !== $this->accessKeySecret) { + $res['accessKeySecret'] = $this->accessKeySecret; + } + if (null !== $this->securityToken) { + $res['securityToken'] = $this->securityToken; + } + if (null !== $this->bearerToken) { + $res['bearerToken'] = $this->bearerToken; + } + if (null !== $this->durationSeconds) { + $res['durationSeconds'] = $this->durationSeconds; + } + if (null !== $this->roleArn) { + $res['roleArn'] = $this->roleArn; + } + if (null !== $this->policy) { + $res['policy'] = $this->policy; + } + if (null !== $this->roleSessionExpiration) { + $res['roleSessionExpiration'] = $this->roleSessionExpiration; + } + if (null !== $this->roleSessionName) { + $res['roleSessionName'] = $this->roleSessionName; + } + if (null !== $this->publicKeyId) { + $res['publicKeyId'] = $this->publicKeyId; + } + if (null !== $this->privateKeyFile) { + $res['privateKeyFile'] = $this->privateKeyFile; + } + if (null !== $this->roleName) { + $res['roleName'] = $this->roleName; + } + if (null !== $this->credentialsURI) { + $res['credentialsURI'] = $this->credentialsURI; + } + if (null !== $this->type) { + $res['type'] = $this->type; + } + if (null !== $this->STSEndpoint) { + $res['STSEndpoint'] = $this->STSEndpoint; + } + if (null !== $this->externalId) { + $res['externalId'] = $this->externalId; + } + return $res; + } + /** + * @param array $map + * @return Config + */ + public static function fromMap($map = []) + { + $model = new self(); + if (isset($map['accessKeyId'])) { + $model->accessKeyId = $map['accessKeyId']; + } + if (isset($map['accessKeySecret'])) { + $model->accessKeySecret = $map['accessKeySecret']; + } + if (isset($map['securityToken'])) { + $model->securityToken = $map['securityToken']; + } + if (isset($map['bearerToken'])) { + $model->bearerToken = $map['bearerToken']; + } + if (isset($map['durationSeconds'])) { + $model->durationSeconds = $map['durationSeconds']; + } + if (isset($map['roleArn'])) { + $model->roleArn = $map['roleArn']; + } + if (isset($map['policy'])) { + $model->policy = $map['policy']; + } + if (isset($map['roleSessionExpiration'])) { + $model->roleSessionExpiration = $map['roleSessionExpiration']; + } + if (isset($map['roleSessionName'])) { + $model->roleSessionName = $map['roleSessionName']; + } + if (isset($map['publicKeyId'])) { + $model->publicKeyId = $map['publicKeyId']; + } + if (isset($map['privateKeyFile'])) { + $model->privateKeyFile = $map['privateKeyFile']; + } + if (isset($map['roleName'])) { + $model->roleName = $map['roleName']; + } + if (isset($map['credentialsURI'])) { + $model->credentialsURI = $map['credentialsURI']; + } + if (isset($map['type'])) { + $model->type = $map['type']; + } + if (isset($map['STSEndpoint'])) { + $model->STSEndpoint = $map['STSEndpoint']; + } + if (isset($map['externalId'])) { + $model->externalId = $map['externalId']; + } + return $model; + } /** + * @description credential type + * @example access_key * @var string */ public $type = 'default'; - public $accessKeyId = ""; + /** + * @description accesskey id + * @var string + */ + public $accessKeyId; + + /** + * @description accesskey secret + * @var string + */ + public $accessKeySecret; + + /** + * @description security token + * @var string + */ + public $securityToken; + + /** + * @description bearer token + * @var string + */ + public $bearerToken; + + /** + * @description role name + * @var string + */ + public $roleName; + + /** + * @description role arn + * @var string + */ + public $roleArn; - public $accessKeySecret = ""; + /** + * @description oidc provider arn + * @var string + */ + public $oidcProviderArn; - public $securityToken = ""; + /** + * @description oidc token file path + * @var string + */ + public $oidcTokenFilePath; - public $bearerToken = ""; + /** + * @description role session expiration + * @example 3600 + * @var int + */ + public $roleSessionExpiration; - public $roleName = ""; + /** + * @description role session name + * @var string + */ + public $roleSessionName; + + /** + * @description role arn policy + * @var string + */ + public $policy; + + /** + * @description external id for ram role arn + * @var string + */ + public $externalId; - public $roleArn = ""; + /** + * @description sts endpoint + * @var string + */ + public $STSEndpoint; - public $roleSessionName = ""; + public $publicKeyId; - public $host = ""; + public $privateKeyFile; - public $publicKeyId = ""; + /** + * @description read timeout + * @var int + */ + public $readTimeout; - public $privateKeyFile = ""; + /** + * @description connection timeout + * @var int + */ + public $connectTimeout; - public $readTimeout = 0; + /** + * @description disable IMDS v1 + * @var bool + */ + public $disableIMDSv1; - public $connectTimeout = 0; + /** + * @description credentials URI + * @var string + */ + public $credentialsURI; - public $certFile = ""; + /** + * @deprecated + */ + public $metadataTokenDuration; - public $certPassword = ""; + /** + * @deprecated + */ + public $durationSeconds; - public $proxy = ""; + /** + * @deprecated + */ + public $host; - public $expiration = 0; + /** + * @deprecated + */ + public $expiration; - public $disableIMDSv1 = false; + /** + * @deprecated + */ + public $certFile = ""; - public $metadataTokenDuration = 21600; + /** + * @deprecated + */ + public $certPassword = ""; - public function __construct($config) - { - foreach ($config as $k => $v) { - $this->{$k} = $v; - } - } + /** + * @internal + */ + public $proxy; } diff --git a/src/Credential/CredentialModel.php b/src/Credential/CredentialModel.php new file mode 100644 index 0000000..ab0fd02 --- /dev/null +++ b/src/Credential/CredentialModel.php @@ -0,0 +1,125 @@ +accessKeyId) { + $res['accessKeyId'] = $this->accessKeyId; + } + if (null !== $this->accessKeySecret) { + $res['accessKeySecret'] = $this->accessKeySecret; + } + if (null !== $this->securityToken) { + $res['securityToken'] = $this->securityToken; + } + if (null !== $this->bearerToken) { + $res['bearerToken'] = $this->bearerToken; + } + if (null !== $this->type) { + $res['type'] = $this->type; + } + return $res; + } + /** + * @param array $map + * @return CredentialModel + */ + public static function fromMap($map = []) + { + $model = new self(); + if (isset($map['accessKeyId'])) { + $model->accessKeyId = $map['accessKeyId']; + } + if (isset($map['accessKeySecret'])) { + $model->accessKeySecret = $map['accessKeySecret']; + } + if (isset($map['securityToken'])) { + $model->securityToken = $map['securityToken']; + } + if (isset($map['bearerToken'])) { + $model->bearerToken = $map['bearerToken']; + } + if (isset($map['type'])) { + $model->type = $map['type']; + } + return $model; + } + /** + * @description accesskey id + * @var string + */ + public $accessKeyId; + + /** + * @description accesskey secret + * @var string + */ + public $accessKeySecret; + + /** + * @description security token + * @var string + */ + public $securityToken; + + /** + * @description bearer token + * @var string + */ + public $bearerToken; + + /** + * @description type + * @example access_key + * @var string + */ + public $type; + + /** + * @return string + */ + public function getAccessKeyId() + { + return $this->accessKeyId; + } + + /** + * @return string + */ + public function getAccessKeySecret() + { + return $this->accessKeySecret; + } + + /** + * @return string + */ + public function getSecurityToken() + { + return $this->securityToken; + } + + /** + * @return string + */ + public function getBearerToken() + { + return $this->bearerToken; + } + + public function getType() + { + return $this->type; + } + +} diff --git a/src/Credentials.php b/src/Credentials.php index 11e68ab..f064b86 100644 --- a/src/Credentials.php +++ b/src/Credentials.php @@ -3,6 +3,8 @@ namespace AlibabaCloud\Credentials; use AlibabaCloud\Credentials\Providers\ChainProvider; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Utils\MockTrait; use ReflectionException; use RuntimeException; @@ -99,4 +101,4 @@ public static function set($name, array $credential) self::$credentials[\strtolower($name)] = \array_change_key_case($credential); } -} +} \ No newline at end of file diff --git a/src/CredentialsInterface.php b/src/CredentialsInterface.php index e8ff0fb..0d109e2 100644 --- a/src/CredentialsInterface.php +++ b/src/CredentialsInterface.php @@ -2,9 +2,11 @@ namespace AlibabaCloud\Credentials; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\SignatureInterface; /** + * @internal This class is intended for internal use within the package. * Interface CredentialsInterface * * @codeCoverageIgnore @@ -12,12 +14,19 @@ interface CredentialsInterface { /** + * @deprecated * @return string */ public function __toString(); /** + * @deprecated * @return SignatureInterface */ public function getSignature(); + + /** + * @return CredentialModel + */ + public function getCredential(); } diff --git a/src/CredentialsProviderWrap.php b/src/CredentialsProviderWrap.php new file mode 100644 index 0000000..23b399e --- /dev/null +++ b/src/CredentialsProviderWrap.php @@ -0,0 +1,75 @@ +typeName = $typeName; + $this->credentialsProvider = $credentialsProvider; + } + + /** + * @inheritDoc + */ + public function getCredential() + { + $credentials = $this->credentialsProvider->getCredentials(); + return new CredentialModel([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'type' => $this->typeName, + ]); + } + + /** + * @param string $name + * @param array $arguments + * + * @return mixed + */ + public function __call($name, $arguments) + { + return $this->credentialsProvider->$name($arguments); + } + + public function __toString() + { + return "credentialsProviderWrap#$this->typeName"; + } + + /** + * @return ShaHmac1Signature + */ + public function getSignature() + { + return null; + } +} \ No newline at end of file diff --git a/src/EcsRamRoleCredential.php b/src/EcsRamRoleCredential.php index 8662f77..ba66c0d 100644 --- a/src/EcsRamRoleCredential.php +++ b/src/EcsRamRoleCredential.php @@ -2,15 +2,18 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\EcsRamRoleProvider; -use AlibabaCloud\Credentials\Request\Request; +use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Request\Request; +use AlibabaCloud\Credentials\Utils\Filter; use Exception; use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; use RuntimeException; /** + * @deprecated * Use the RAM role of an ECS instance to complete the authentication. */ class EcsRamRoleCredential implements CredentialsInterface @@ -37,7 +40,7 @@ class EcsRamRoleCredential implements CredentialsInterface * * @param $role_name */ - public function __construct($role_name = null, $disable_imdsv1 = false, $metadata_token_duration = 21600 ) + public function __construct($role_name = null, $disable_imdsv1 = false, $metadata_token_duration = 21600) { Filter::roleName($role_name); @@ -47,8 +50,6 @@ public function __construct($role_name = null, $disable_imdsv1 = false, $metadat $this->disableIMDSv1 = $disable_imdsv1; - Filter::metadataTokenDuration($metadata_token_duration); - $this->metadataTokenDuration = $metadata_token_duration; } @@ -75,8 +76,8 @@ public function getRoleName() public function getRoleNameFromMeta() { $options = [ - 'http_errors' => false, - 'timeout' => 1, + 'http_errors' => false, + 'timeout' => 1, 'connect_timeout' => 1, ]; @@ -94,7 +95,7 @@ public function getRoleNameFromMeta() throw new RuntimeException('Error retrieving credentials from result: ' . $result->getBody()); } - $role_name = (string)$result; + $role_name = (string) $result; if (!$role_name) { throw new RuntimeException('Error retrieving credentials from result is empty'); } @@ -129,17 +130,18 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return AlibabaCloud\Credentials\Providers\Credentials * @throws Exception * @throws GuzzleException */ protected function getSessionCredential() { - $config = [ + $params = [ + "roleName" => $this->roleName, 'disableIMDSv1' => $this->disableIMDSv1, 'metadataTokenDuration' => $this->metadataTokenDuration, ]; - return (new EcsRamRoleProvider($this, $config))->get(); + return (new EcsRamRoleCredentialsProvider($params))->getCredentials(); } /** @@ -180,4 +182,18 @@ public function isDisableIMDSv1() return $this->disableIMDSv1; } + /** + * @inheritDoc + */ + public function getCredential() + { + $credentials = $this->getSessionCredential(); + return new CredentialModel([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'type' => 'ecs_ram_role', + ]); + } + } diff --git a/src/Filter.php b/src/Filter.php deleted file mode 100644 index 1811a33..0000000 --- a/src/Filter.php +++ /dev/null @@ -1,151 +0,0 @@ -filterProfileName($params); + } + + private function filterProfileName(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_PROFILE')) { + $this->profileName = Helper::env('ALIBABA_CLOUD_PROFILE'); + } + + if (isset($params['profileName'])) { + $this->profileName = $params['profileName']; + } + } + + /** + * @return bool + */ + private function shouldReloadCredentialsProvider() + { + if (is_null($this->credentialsProvider)) { + return true; + } + + return false; + } + + /** + * @return CredentialsProvider + */ + protected function reloadCredentialsProvider($profileFile, $profileName) + { + if (!Helper::inOpenBasedir($profileFile)) { + throw new RuntimeException('Unable to open credentials file: ' . $profileFile); + } + + if (!\is_readable($profileFile) || !\is_file($profileFile)) { + throw new RuntimeException('Credentials file is not readable: ' . $profileFile); + } + + $jsonContent = \file_get_contents($profileFile); + $fileArray = json_decode($jsonContent, true); + + if (\is_array($fileArray) && !empty($fileArray)) { + if (is_null($profileName) || $profileName === '') { + $profileName = $fileArray['current']; + } + if (isset($fileArray['profiles'])) { + foreach ($fileArray['profiles'] as $profile) { + if (Helper::unsetReturnNull($profile, 'name') === $profileName) { + switch (Helper::unsetReturnNull($profile, 'mode')) { + case 'AK': + return new StaticAKCredentialsProvider([ + 'accessKeyId' => Helper::unsetReturnNull($profile, 'access_key_id'), + 'accessKeySecret' => Helper::unsetReturnNull($profile, 'access_key_secret'), + ]); + case 'RamRoleArn': + $innerProvider = new StaticAKCredentialsProvider([ + 'accessKeyId' => Helper::unsetReturnNull($profile, 'access_key_id'), + 'accessKeySecret' => Helper::unsetReturnNull($profile, 'access_key_secret'), + ]); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $innerProvider, + 'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'), + 'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'), + 'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'), + 'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'), + ]); + case 'EcsRamRole': + return new EcsRamRoleCredentialsProvider([ + 'roleName' => Helper::unsetReturnNull($profile, 'ram_role_name'), + ]); + case 'OIDC': + return new OIDCRoleArnCredentialsProvider([ + 'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'), + 'oidcProviderArn' => Helper::unsetReturnNull($profile, 'oidc_provider_arn'), + 'oidcTokenFilePath' => Helper::unsetReturnNull($profile, 'oidc_token_file'), + 'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'), + 'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'), + 'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'), + ]); + case 'ChainableRamRoleArn': + $previousProvider = $this->reloadCredentialsProvider($profileFile, Helper::unsetReturnNull($profile, 'source_profile')); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $previousProvider, + 'roleArn' => Helper::unsetReturnNull($profile, 'ram_role_arn'), + 'roleSessionName' => Helper::unsetReturnNull($profile, 'ram_session_name'), + 'durationSeconds' => Helper::unsetReturnNull($profile, 'expired_seconds'), + 'stsRegionId' => Helper::unsetReturnNull($profile, 'sts_region'), + ]); + default: + throw new RuntimeException('Unsupported credential mode from CLI credentials file: ' . Helper::unsetReturnNull($profile, 'mode')); + } + } + } + } + } + throw new RuntimeException('Failed to get credential from CLI credentials file: ' . $profileFile); + } + /** + * Get credential. + * + * @return Credentials + * @throws RuntimeException + */ + public function getCredentials() + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_CLI_PROFILE_DISABLED') && Helper::env('ALIBABA_CLOUD_CLI_PROFILE_DISABLED') === true) { + throw new RuntimeException('CLI credentials file is disabled'); + } + $cliProfileFile = self::getDefaultFile(); + if ($this->shouldReloadCredentialsProvider()) { + $this->credentialsProvider = $this->reloadCredentialsProvider($cliProfileFile, $this->profileName); + } + + $credentials = $this->credentialsProvider->getCredentials(); + return new Credentials([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'providerName' => $this->getProviderName() . '/' . $this->credentialsProvider->getProviderName(), + ]); + } + + /** + * Get the default credential file. + * + * @return string + */ + private function getDefaultFile() + { + return Helper::getHomeDirectory() . + DIRECTORY_SEPARATOR . + '.aliyun' . + DIRECTORY_SEPARATOR . + 'config.json'; + } + + /** + * @return string + */ + public function getProviderName() + { + return 'cli_profile'; + } +} diff --git a/src/Providers/ChainProvider.php b/src/Providers/ChainProvider.php index 83cb650..d6c1540 100644 --- a/src/Providers/ChainProvider.php +++ b/src/Providers/ChainProvider.php @@ -3,12 +3,13 @@ namespace AlibabaCloud\Credentials\Providers; use AlibabaCloud\Credentials\Credentials; -use AlibabaCloud\Credentials\Helper; +use AlibabaCloud\Credentials\Utils\Helper; use Closure; use InvalidArgumentException; use RuntimeException; /** + * @deprecated * Class ChainProvider * * @package AlibabaCloud\Credentials\Providers @@ -184,4 +185,4 @@ public static function instance() } }; } -} +} \ No newline at end of file diff --git a/src/Providers/Credentials.php b/src/Providers/Credentials.php new file mode 100644 index 0000000..bfd4fe3 --- /dev/null +++ b/src/Providers/Credentials.php @@ -0,0 +1,87 @@ + $v) { + $this->{$k} = $v; + } + } + } + + /** + * @return string + */ + public function getAccessKeyId() + { + return $this->accessKeyId; + } + + /** + * @return string + */ + public function getAccessKeySecret() + { + return $this->accessKeySecret; + } + + /** + * @return string + */ + public function getSecurityToken() + { + return $this->securityToken; + } + + /** + * @return int + */ + public function getExpiration() + { + return $this->expiration; + } + + /** + * @return string + */ + public function getProviderName() + { + return $this->providerName; + } +} diff --git a/src/Providers/CredentialsProvider.php b/src/Providers/CredentialsProvider.php new file mode 100644 index 0000000..ddbd1a1 --- /dev/null +++ b/src/Providers/CredentialsProvider.php @@ -0,0 +1,24 @@ +filterReuseLastProviderEnabled($params); + $this->createDefaultChain(); + Filter::reuseLastProviderEnabled($this->reuseLastProviderEnabled); + } + + private function filterReuseLastProviderEnabled(array $params) + { + $this->reuseLastProviderEnabled = true; + if (isset($params['reuseLastProviderEnabled'])) { + $this->reuseLastProviderEnabled = $params['reuseLastProviderEnabled']; + } + } + + private function createDefaultChain() + { + self::$defaultProviders = [ + new EnvironmentVariableCredentialsProvider(), + ]; + if ( + Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN') + && Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_PROVIDER_ARN') + && Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_TOKEN_FILE') + ) { + array_push( + self::$defaultProviders, + new OIDCRoleArnCredentialsProvider() + ); + } + array_push( + self::$defaultProviders, + new CLIProfileCredentialsProvider() + ); + array_push( + self::$defaultProviders, + new ProfileCredentialsProvider() + ); + if (Helper::envNotEmpty('ALIBABA_CLOUD_ECS_METADATA')) { + array_push( + self::$defaultProviders, + new EcsRamRoleCredentialsProvider() + ); + } + if (Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_URI')) { + array_push( + self::$defaultProviders, + new URLCredentialsProvider() + ); + } + } + + /** + * @param CredentialsProvider ...$providers + */ + public static function set(...$providers) + { + if (empty($providers)) { + throw new InvalidArgumentException('No providers in chain'); + } + + foreach ($providers as $provider) { + if (!$provider instanceof CredentialsProvider) { + throw new InvalidArgumentException('Providers must all be CredentialsProvider'); + } + } + + self::$customChain = $providers; + } + + /** + * @return bool + */ + public static function hasCustomChain() + { + return (bool) self::$customChain; + } + + public static function flush() + { + self::$customChain = []; + } + + /** + * Get credential. + * + * @return Credentials + * @throws RuntimeException + */ + public function getCredentials() + { + if ($this->reuseLastProviderEnabled && !is_null($this->lastUsedCredentialsProvider)) { + $credentials = $this->lastUsedCredentialsProvider->getCredentials(); + return new Credentials([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'providerName' => $this->getProviderName() . '/' . $this->lastUsedCredentialsProvider->getProviderName(), + ]); + } + + $providerChain = array_merge( + self::$customChain, + self::$defaultProviders + ); + + $exceptionMessages = []; + + foreach ($providerChain as $provider) { + try { + $credentials = $provider->getCredentials(); + $this->lastUsedCredentialsProvider = $provider; + return new Credentials([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'providerName' => $this->getProviderName() . '/' . $provider->getProviderName(), + ]); + } catch (Exception $exception) { + array_push($exceptionMessages, basename(str_replace('\\', '/', get_class($provider))) . ': ' . $exception->getMessage()); + } + } + throw new RuntimeException('Unable to load credentials from any of the providers in the chain: ' . implode(', ', $exceptionMessages)); + + } + + /** + * @inheritDoc + */ + public function getProviderName() + { + return "default"; + } +} \ No newline at end of file diff --git a/src/Providers/EcsRamRoleCredentialsProvider.php b/src/Providers/EcsRamRoleCredentialsProvider.php new file mode 100644 index 0000000..d04fc53 --- /dev/null +++ b/src/Providers/EcsRamRoleCredentialsProvider.php @@ -0,0 +1,256 @@ +filterOptions($options); + $this->filterRoleName($params); + $this->filterDisableECSIMDSv1($params); + Filter::roleName($this->roleName); + Filter::disableIMDSv1($this->disableIMDSv1); + } + + private function filterOptions(array $options) + { + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + + if (isset($options['readTimeout'])) { + $this->readTimeout = $options['readTimeout']; + } + + Filter::timeout($this->connectTimeout, $this->readTimeout); + } + + private function filterRoleName(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ECS_METADATA')) { + $this->roleName = Helper::env('ALIBABA_CLOUD_ECS_METADATA'); + } + + if (isset($params['roleName'])) { + $this->roleName = $params['roleName']; + } + + if (is_null($this->roleName) || $this->roleName === '') { + $this->roleName = $this->getRoleNameFromMeta(); + } + } + + private function filterDisableECSIMDSv1($params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_IMDSV1_DISABLED')) { + $this->disableIMDSv1 = Helper::env('ALIBABA_CLOUD_IMDSV1_DISABLED') === true ? true : false; + } + + if (isset($params['disableIMDSv1'])) { + $this->disableIMDSv1 = $params['disableIMDSv1']; + } + } + + /** + * Get credentials by request. + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException + * @throws GuzzleException + */ + public function refreshCredentials() + { + $url = $this->metadataHost . $this->ecsUri . $this->roleName; + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $metadataToken = $this->getMetadataToken(); + if (!is_null($metadataToken)) { + $options['headers']['X-aliyun-ecs-metadata-token'] = $metadataToken; + } + + $result = Request::createClient()->request('GET', $url, $options); + + if ($result->getStatusCode() === 404) { + throw new InvalidArgumentException('The role was not found in the instance' . (string) $result); + } + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error refreshing credentials from IMDS, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result); + } + + $credentials = $result->toArray(); + + if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) { + throw new RuntimeException('Error retrieving credentials from IMDS result:' . $result->toJson()); + } + + if (!isset($credentials['Code']) || $credentials['Code'] !== 'Success') { + throw new RuntimeException('Error retrieving credentials from IMDS result, Code is not Success:' . $result->toJson()); + } + + return $credentials; + } + + /** + * @return string + * @throws InvalidArgumentException + * @throws RuntimeException + * @throws GuzzleException + */ + private function getRoleNameFromMeta() + { + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $metadataToken = $this->getMetadataToken(); + if (!is_null($metadataToken)) { + $options['headers']['X-aliyun-ecs-metadata-token'] = $metadataToken; + } + + $result = Request::createClient()->request( + 'GET', + 'http://100.100.100.200/latest/meta-data/ram/security-credentials/', + $options + ); + + if ($result->getStatusCode() === 404) { + throw new InvalidArgumentException('The role name was not found in the instance' . (string) $result); + } + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error retrieving role name from result: ' . (string) $result); + } + + $role_name = (string) $result; + if (!$role_name) { + throw new RuntimeException('Error retrieving role name from result is empty'); + } + + return $role_name; + } + + /** + * Get metadata token by request. + * + * @return string + * @throws RuntimeException + * @throws GuzzleException + */ + private function getMetadataToken() + { + $url = $this->metadataHost . $this->metadataTokenUri; + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + $options['headers']['X-aliyun-ecs-metadata-token-ttl-seconds'] = $this->metadataTokenDuration; + + $result = Request::createClient()->request('PUT', $url, $options); + + if ($result->getStatusCode() != 200) { + if ($this->disableIMDSv1) { + throw new RuntimeException('Failed to get token from ECS Metadata Service. HttpCode= ' . $result->getStatusCode()); + } + return null; + } + return (string) $result; + } + + + /** + * @return string + */ + public function key() + { + return 'ecs_ram_role#roleName#' . $this->roleName; + } + + /** + * @return string + */ + public function getProviderName() + { + return 'ecs_ram_role'; + } + + /** + * @return string + */ + public function getRoleName() + { + return $this->roleName; + } + + /** + * @return bool + */ + public function isDisableIMDSv1() + { + return $this->disableIMDSv1; + } +} diff --git a/src/Providers/EcsRamRoleProvider.php b/src/Providers/EcsRamRoleProvider.php deleted file mode 100644 index b2c53b7..0000000 --- a/src/Providers/EcsRamRoleProvider.php +++ /dev/null @@ -1,180 +0,0 @@ -getCredentialsInCache(); - - if ($result === null) { - $result = $this->request(); - - if (!isset($result['AccessKeyId'], $result['AccessKeySecret'], $result['SecurityToken'])) { - throw new RuntimeException($this->error); - } - - $this->cache($result->toArray()); - } - - return new StsCredential( - $result['AccessKeyId'], - $result['AccessKeySecret'], - strtotime($result['Expiration']), - $result['SecurityToken'] - ); - } - - - protected function getDisableECSIMDSv1() - { - if (Helper::envNotEmpty('ALIBABA_CLOUD_IMDSV1_DISABLE')) { - return Helper::env('ALIBABA_CLOUD_IMDSV1_DISABLE') === true ? true : false; - } - if(isset($this->config['disableIMDSv1'])) { - return $this->config['disableIMDSv1']; - } - return false; - } - - /** - * Get credentials by request. - * - * @return ResponseInterface - * @throws Exception - * @throws GuzzleException - */ - public function request() - { - $credential = $this->credential; - $url = $this->metadataHost . $this->ecsUri . $credential->getRoleName(); - - $options = [ - 'http_errors' => false, - 'timeout' => 1, - 'connect_timeout' => 1, - ]; - - $this->metadataToken = $this->refreshMetadataToken(); - if(!is_null($this->metadataToken)) { - $options['headers']['X-aliyun-ecs-metadata-token'] = $this->metadataToken; - } - - $result = Request::createClient()->request('GET', $url, $options); - - if ($result->getStatusCode() === 404) { - $message = 'The role was not found in the instance'; - throw new InvalidArgumentException($message); - } - - if ($result->getStatusCode() !== 200) { - throw new RuntimeException('Error retrieving credentials from result: ' . $result->toJson()); - } - - return $result; - } - - /** - * Get metadata token by request. - * - * @return bool - * @throws Exception - * @throws GuzzleException - */ - protected function refreshMetadataToken() - { - if(!$this->needToRefresh()) { - return $this->metadataToken; - } - $credential = $this->credential; - $url = $this->metadataHost . $this->metadataTokenUri; - $tmpTime = $this->staleTime; - $this->staleTime = time() + $this->config['metadataTokenDuration']; - $options = [ - 'http_errors' => false, - 'timeout' => 1, - 'connect_timeout' => 1, - 'headers' => [ - 'X-aliyun-ecs-metadata-token-ttl-seconds' => $this->config['metadataTokenDuration'], - ], - ]; - - $result = Request::createClient()->request('PUT', $url, $options); - - if ($result->getStatusCode() != 200) { - $this->staleTime = $tmpTime; - if ($this->getDisableECSIMDSv1()) { - throw new RuntimeException('Failed to get token from ECS Metadata Service. HttpCode= ' . $result->getStatusCode()); - } - return null; - } - return (string) $result->getBody(); - } - - - /** - * @return boolean - */ - protected function needToRefresh() - { - return \time() >= $this->staleTime; - } -} diff --git a/src/Providers/EnvironmentVariableCredentialsProvider.php b/src/Providers/EnvironmentVariableCredentialsProvider.php new file mode 100644 index 0000000..5baad30 --- /dev/null +++ b/src/Providers/EnvironmentVariableCredentialsProvider.php @@ -0,0 +1,61 @@ + $accessKeyId, + 'accessKeySecret' => $accessKeySecret, + 'securityToken' => $securityToken, + 'providerName' => $this->getProviderName(), + ]); + } + + /** + * @inheritDoc + */ + public function getProviderName() + { + return "env"; + } +} \ No newline at end of file diff --git a/src/Providers/OIDCRoleArnCredentialsProvider.php b/src/Providers/OIDCRoleArnCredentialsProvider.php new file mode 100644 index 0000000..9fb0706 --- /dev/null +++ b/src/Providers/OIDCRoleArnCredentialsProvider.php @@ -0,0 +1,257 @@ +filterOptions($options); + $this->filterRoleArn($params); + $this->filterOIDCProviderArn($params); + $this->filterOIDCTokenFilePath($params); + $this->filterRoleSessionName($params); + $this->filterDurationSeconds($params); + $this->filterPolicy($params); + $this->filterSTSEndpoint($params); + } + + private function filterRoleArn(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN')) { + $this->roleArn = Helper::env('ALIBABA_CLOUD_ROLE_ARN'); + } + + if (isset($params['roleArn'])) { + $this->roleArn = $params['roleArn']; + } + + Filter::roleArn($this->roleArn); + } + + private function filterOIDCProviderArn(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_PROVIDER_ARN')) { + $this->oidcProviderArn = Helper::env('ALIBABA_CLOUD_OIDC_PROVIDER_ARN'); + } + + if (isset($params['oidcProviderArn'])) { + $this->oidcProviderArn = $params['oidcProviderArn']; + } + + Filter::oidcProviderArn($this->oidcProviderArn); + } + + private function filterOIDCTokenFilePath(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_OIDC_TOKEN_FILE')) { + $this->oidcTokenFilePath = Helper::env('ALIBABA_CLOUD_OIDC_TOKEN_FILE'); + } + + if (isset($params['oidcTokenFilePath'])) { + $this->oidcTokenFilePath = $params['oidcTokenFilePath']; + } + + Filter::oidcTokenFilePath($this->oidcTokenFilePath); + } + + private function filterRoleSessionName(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_SESSION_NAME')) { + $this->roleSessionName = Helper::env('ALIBABA_CLOUD_ROLE_SESSION_NAME'); + } + + if (isset($params['roleSessionName'])) { + $this->roleSessionName = $params['roleSessionName']; + } + + if (is_null($this->roleSessionName) || $this->roleSessionName === '') { + $this->roleSessionName = 'phpSdkRoleSessionName'; + } + } + + private function filterDurationSeconds(array $params) + { + if (isset($params['durationSeconds'])) { + if (is_int($params['durationSeconds'])) { + $this->durationSeconds = $params['durationSeconds']; + } + } + if ($this->durationSeconds < 900) { + throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration'); + } + } + + private function filterPolicy(array $params) + { + if (isset($params['policy'])) { + if (is_string($params['policy'])) { + $this->policy = $params['policy']; + } + + if (is_array($params['policy'])) { + $this->policy = json_encode($params['policy']); + } + } + } + + private function filterSTSEndpoint(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_STS_REGION')) { + $this->stsEndpoint = 'sts' . Helper::env('ALIBABA_CLOUD_STS_REGION') . '.aliyuncs.com'; + } + + if (isset($params['stsRegionId'])) { + $this->stsEndpoint = 'sts' . $params['stsRegionId'] . '.aliyuncs.com'; + } + + if (isset($params['stsEndpoint'])) { + $this->stsEndpoint = $params['stsEndpoint']; + } + + if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') { + $this->stsEndpoint = 'sts.aliyuncs.com'; + } + } + + private function filterOptions(array $options) + { + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + + if (isset($options['readTimeout'])) { + $this->readTimeout = $options['readTimeout']; + } + + Filter::timeout($this->connectTimeout, $this->readTimeout); + } + + /** + * Get credentials by request. + * + * @return array + * @throws RuntimeException + * @throws GuzzleException + */ + public function refreshCredentials() + { + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $options['query']['Action'] = 'AssumeRoleWithOIDC'; + $options['query']['Version'] = '2015-04-01'; + $options['query']['Format'] = 'JSON'; + $options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z'); + $options['query']['RoleArn'] = $this->roleArn; + $options['query']['OIDCProviderArn'] = $this->oidcProviderArn; + try { + $oidcToken = file_get_contents($this->oidcTokenFilePath); + $options['query']['OIDCToken'] = $oidcToken; + } catch (Exception $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } + $options['query']['RoleSessionName'] = $this->roleSessionName; + $options['query']['DurationSeconds'] = (string) $this->durationSeconds; + if (!is_null($this->policy)) { + $options['query']['Policy'] = $this->policy; + } + + $url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint); + + $result = Request::createClient()->request('POST', $url, $options); + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error refreshing credentials from OIDC, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result); + } + + $json = $result->toArray(); + $credentials = $json['Credentials']; + + if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) { + throw new RuntimeException('Error retrieving credentials from OIDC result:' . $result->toJson()); + } + + return $credentials; + } + + public function key() + { + return 'oidc_role_arn#roleArn#' . $this->roleArn . '#oidcProviderArn#' . $this->oidcProviderArn . '#roleSessionName#' . $this->roleSessionName; + } + + public function getProviderName() + { + return 'oidc_role_arn'; + } +} diff --git a/src/Providers/ProfileCredentialsProvider.php b/src/Providers/ProfileCredentialsProvider.php new file mode 100644 index 0000000..ab07efe --- /dev/null +++ b/src/Providers/ProfileCredentialsProvider.php @@ -0,0 +1,188 @@ +filterProfileName($params); + $this->filterProfileFile(); + } + + private function filterProfileName(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_PROFILE')) { + $this->profileName = Helper::env('ALIBABA_CLOUD_PROFILE'); + } + + if (isset($params['profileName'])) { + $this->profileName = $params['profileName']; + } + + if (is_null($this->profileName) || $this->profileName === '') { + $this->profileName = 'default'; + } + } + + private function filterProfileFile() + { + $this->profileFile = Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_FILE'); + + if (!$this->profileFile) { + $this->profileFile = self::getDefaultFile(); + } + } + + /** + * @return bool + */ + private function shouldReloadCredentialsProvider() + { + if (is_null($this->credentialsProvider)) { + return true; + } + + return false; + } + + /** + * @return CredentialsProvider + */ + private function reloadCredentialsProvider($profileFile, $profileName) + { + if (!Helper::inOpenBasedir($profileFile)) { + throw new RuntimeException('Unable to open credentials file: ' . $profileFile); + } + + if (!\is_readable($profileFile) || !\is_file($profileFile)) { + throw new RuntimeException('Credentials file is not readable: ' . $profileFile); + } + + $fileArray = \parse_ini_file($profileFile, true); + + if (\is_array($fileArray) && !empty($fileArray)) { + $credentialsConfigures = []; + foreach (\array_change_key_case($fileArray) as $name => $configures) { + if ($name === $profileName) { + $credentialsConfigures = $configures; + break; + } + } + if (\is_array($credentialsConfigures) && !empty($credentialsConfigures)) { + switch (Helper::unsetReturnNull($credentialsConfigures, 'type')) { + case 'access_key': + return new StaticAKCredentialsProvider([ + 'accessKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_id'), + 'accessKeySecret' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_secret'), + ]); + case 'ram_role_arn': + $innerProvider = new StaticAKCredentialsProvider([ + 'accessKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_id'), + 'accessKeySecret' => Helper::unsetReturnNull($credentialsConfigures, 'access_key_secret'), + ]); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $innerProvider, + 'roleArn' => Helper::unsetReturnNull($credentialsConfigures, 'role_arn'), + 'roleSessionName' => Helper::unsetReturnNull($credentialsConfigures, 'role_session_name'), + 'policy' => Helper::unsetReturnNull($credentialsConfigures, 'policy'), + ]); + case 'ecs_ram_role': + return new EcsRamRoleCredentialsProvider([ + 'roleName' => Helper::unsetReturnNull($credentialsConfigures, 'role_name'), + ]); + case 'oidc_role_arn': + return new OIDCRoleArnCredentialsProvider([ + 'roleArn' => Helper::unsetReturnNull($credentialsConfigures, 'role_arn'), + 'oidcProviderArn' => Helper::unsetReturnNull($credentialsConfigures, 'oidc_provider_arn'), + 'oidcTokenFilePath' => Helper::unsetReturnNull($credentialsConfigures, 'oidc_token_file_path'), + 'roleSessionName' => Helper::unsetReturnNull($credentialsConfigures, 'role_session_name'), + 'policy' => Helper::unsetReturnNull($credentialsConfigures, 'policy'), + ]); + case 'rsa_key_pair': + return new RsaKeyPairCredentialsProvider([ + 'publicKeyId' => Helper::unsetReturnNull($credentialsConfigures, 'public_key_id'), + 'privateKeyFile' => Helper::unsetReturnNull($credentialsConfigures, 'private_key_file'), + ]); + default: + throw new RuntimeException('Unsupported credential type from credentials file: ' . Helper::unsetReturnNull($credentialsConfigures, 'type')); + } + } + } + throw new RuntimeException('Failed to get credential from credentials file: ' . $profileFile); + } + /** + * Get credential. + * + * @return Credentials + * @throws RuntimeException + */ + public function getCredentials() + { + if ($this->shouldReloadCredentialsProvider()) { + $this->credentialsProvider = $this->reloadCredentialsProvider($this->profileFile, $this->profileName); + } + + $credentials = $this->credentialsProvider->getCredentials(); + return new Credentials([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'providerName' => $this->getProviderName() . '/' . $this->credentialsProvider->getProviderName(), + ]); + + } + + /** + * Get the default credential file. + * + * @return string + */ + private function getDefaultFile() + { + return Helper::getHomeDirectory() . + DIRECTORY_SEPARATOR . + '.alibabacloud' . + DIRECTORY_SEPARATOR . + 'credentials'; + } + + /** + * @return string + */ + public function getProviderName() + { + return 'profile'; + } +} diff --git a/src/Providers/Provider.php b/src/Providers/Provider.php deleted file mode 100644 index e1e869d..0000000 --- a/src/Providers/Provider.php +++ /dev/null @@ -1,82 +0,0 @@ -credential = $credential; - $this->config = $config; - } - - /** - * Get the credentials from the cache in the validity period. - * - * @return array|null - */ - public function getCredentialsInCache() - { - if (isset(self::$credentialsCache[(string)$this->credential])) { - $result = self::$credentialsCache[(string)$this->credential]; - if (\strtotime($result['Expiration']) - \time() >= $this->expirationSlot) { - return $result; - } - } - - return null; - } - - /** - * Cache credentials. - * - * @param array $credential - */ - protected function cache(array $credential) - { - self::$credentialsCache[(string)$this->credential] = $credential; - } -} diff --git a/src/Providers/RamRoleArnCredentialsProvider.php b/src/Providers/RamRoleArnCredentialsProvider.php new file mode 100644 index 0000000..d18bdf3 --- /dev/null +++ b/src/Providers/RamRoleArnCredentialsProvider.php @@ -0,0 +1,310 @@ +filterOptions($options); + $this->filterCredentials($params); + $this->filterRoleArn($params); + $this->filterRoleSessionName($params); + $this->filterDurationSeconds($params); + $this->filterPolicy($params); + $this->filterExternalId($params); + $this->filterSTSEndpoint($params); + } + + private function filterRoleArn(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_ARN')) { + $this->roleArn = Helper::env('ALIBABA_CLOUD_ROLE_ARN'); + } + + if (isset($params['roleArn'])) { + $this->roleArn = $params['roleArn']; + } + + Filter::roleArn($this->roleArn); + } + + private function filterRoleSessionName(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ROLE_SESSION_NAME')) { + $this->roleSessionName = Helper::env('ALIBABA_CLOUD_ROLE_SESSION_NAME'); + } + + if (isset($params['roleSessionName'])) { + $this->roleSessionName = $params['roleSessionName']; + } + + if (is_null($this->roleSessionName) || $this->roleSessionName === '') { + $this->roleSessionName = 'phpSdkRoleSessionName'; + } + } + + private function filterDurationSeconds(array $params) + { + if (isset($params['durationSeconds'])) { + if (is_int($params['durationSeconds'])) { + $this->durationSeconds = $params['durationSeconds']; + } + } + if ($this->durationSeconds < 900) { + throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration'); + } + } + + private function filterPolicy(array $params) + { + if (isset($params['policy'])) { + if (is_string($params['policy'])) { + $this->policy = $params['policy']; + } + + if (is_array($params['policy'])) { + $this->policy = json_encode($params['policy']); + } + } + } + + private function filterExternalId(array $params) + { + if (isset($params['externalId'])) { + if (is_string($params['externalId'])) { + $this->externalId = $params['externalId']; + } + } + } + + private function filterSTSEndpoint(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_STS_REGION')) { + $this->stsEndpoint = 'sts.' . Helper::env('ALIBABA_CLOUD_STS_REGION') . '.aliyuncs.com'; + } + + if (isset($params['stsRegionId'])) { + $this->stsEndpoint = 'sts.' . $params['stsRegionId'] . '.aliyuncs.com'; + } + + if (isset($params['stsEndpoint'])) { + $this->stsEndpoint = $params['stsEndpoint']; + } + + if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') { + $this->stsEndpoint = 'sts.aliyuncs.com'; + } + } + + private function filterCredentials(array $params) + { + if (isset($params['credentialsProvider'])) { + if (!($params['credentialsProvider'] instanceof CredentialsProvider)) { + throw new InvalidArgumentException('Invalid credentialsProvider option for ram_role_arn'); + } + $this->credentialsProvider = $params['credentialsProvider']; + } else if (isset($params['accessKeyId']) && isset($params['accessKeySecret']) && isset($params['securityToken'])) { + Filter::accessKey($params['accessKeyId'], $params['accessKeySecret']); + Filter::securityToken($params['securityToken']); + $this->credentialsProvider = new StaticSTSCredentialsProvider($params); + } else if (isset($params['accessKeyId']) && isset($params['accessKeySecret'])) { + Filter::accessKey($params['accessKeyId'], $params['accessKeySecret']); + $this->credentialsProvider = new StaticAKCredentialsProvider($params); + } else { + throw new InvalidArgumentException('Missing required credentials option for ram_role_arn'); + } + } + + private function filterOptions(array $options) + { + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + + if (isset($options['readTimeout'])) { + $this->readTimeout = $options['readTimeout']; + } + + Filter::timeout($this->connectTimeout, $this->readTimeout); + } + + /** + * Get credentials by request. + * + * @return array + * @throws RuntimeException + * @throws GuzzleException + */ + public function refreshCredentials() + { + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $options['query']['Action'] = 'AssumeRole'; + $options['query']['Version'] = '2015-04-01'; + $options['query']['Format'] = 'JSON'; + $options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z'); + $options['query']['SignatureMethod'] = 'HMAC-SHA1'; + $options['query']['SignatureVersion'] = '1.0'; + $options['query']['SignatureNonce'] = Request::uuid(json_encode($options['query'])); + $options['query']['RoleArn'] = $this->roleArn; + $options['query']['RoleSessionName'] = $this->roleSessionName; + $options['query']['DurationSeconds'] = (string) $this->durationSeconds; + if (!is_null($this->policy) && $this->policy !== '') { + $options['query']['Policy'] = $this->policy; + } + if (!is_null($this->externalId) && $this->externalId !== '') { + $options['query']['ExternalId'] = $this->externalId; + } + + $sessionCredentials = $this->credentialsProvider->getCredentials(); + $options['query']['AccessKeyId'] = $sessionCredentials->getAccessKeyId(); + if (!is_null($sessionCredentials->getSecurityToken())) { + $options['query']['SecurityToken'] = $sessionCredentials->getSecurityToken(); + } + $options['query']['Signature'] = Request::shaHmac1sign( + Request::signString('GET', $options['query']), + $sessionCredentials->getAccessKeySecret() . '&' + ); + + $url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint); + + $result = Request::createClient()->request('GET', $url, $options); + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error refreshing credentials from RamRoleArn, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result); + } + + $json = $result->toArray(); + $credentials = $json['Credentials']; + + if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken'])) { + throw new RuntimeException('Error retrieving credentials from RamRoleArn result:' . $result->toJson()); + } + + return $credentials; + } + + public function key() + { + $credentials = $this->credentialsProvider->getCredentials(); + return 'ram_role_arn#credential#' . $credentials->getAccessKeyId() . '#roleArn#' . $this->roleArn . '#roleSessionName#' . $this->roleSessionName; + } + + public function getProviderName() + { + return 'ram_role_arn/' . $this->credentialsProvider->getProviderName(); + } + + /** + * @return string + */ + public function getRoleArn() + { + return $this->roleArn; + } + + /** + * @return string + */ + public function getRoleSessionName() + { + return $this->roleSessionName; + } + + /** + * @return string + */ + public function getPolicy() + { + return $this->policy; + } + + /** + * @deprecated + * @return string + */ + public function getOriginalAccessKeyId() + { + return $this->credentialsProvider->getCredentials()->getAccessKeyId(); + } + + /** + * @deprecated + * @return string + */ + public function getOriginalAccessKeySecret() + { + return $this->credentialsProvider->getCredentials()->getAccessKeySecret(); + } +} diff --git a/src/Providers/RamRoleArnProvider.php b/src/Providers/RamRoleArnProvider.php deleted file mode 100644 index c6a8729..0000000 --- a/src/Providers/RamRoleArnProvider.php +++ /dev/null @@ -1,49 +0,0 @@ -getCredentialsInCache(); - - if (null === $credential) { - $result = (new AssumeRole($this->credential))->request(); - - if ($result->getStatusCode() !== 200) { - throw new RuntimeException(isset($result['Message']) ? $result['Message'] : (string)$result->getBody()); - } - - if (!isset($result['Credentials']['AccessKeyId'], - $result['Credentials']['AccessKeySecret'], - $result['Credentials']['SecurityToken'])) { - throw new RuntimeException($this->error); - } - - $credential = $result['Credentials']; - $this->cache($credential); - } - - return new StsCredential( - $credential['AccessKeyId'], - $credential['AccessKeySecret'], - strtotime($credential['Expiration']), - $credential['SecurityToken'] - ); - } -} diff --git a/src/Providers/RsaKeyPairCredentialsProvider.php b/src/Providers/RsaKeyPairCredentialsProvider.php new file mode 100644 index 0000000..a6fe96b --- /dev/null +++ b/src/Providers/RsaKeyPairCredentialsProvider.php @@ -0,0 +1,193 @@ +filterOptions($options); + $this->filterDurationSeconds($params); + $this->filterSTSEndpoint($params); + $this->publicKeyId = isset($params['publicKeyId']) ? $params['publicKeyId'] : null; + $privateKeyFile = isset($params['privateKeyFile']) ? $params['privateKeyFile'] : null; + Filter::publicKeyId($this->publicKeyId); + Filter::privateKeyFile($privateKeyFile); + + try { + $this->privateKey = file_get_contents($privateKeyFile); + } catch (Exception $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } + } + + private function filterOptions(array $options) + { + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + + if (isset($options['readTimeout'])) { + $this->readTimeout = $options['readTimeout']; + } + + Filter::timeout($this->connectTimeout, $this->readTimeout); + } + + private function filterDurationSeconds(array $params) + { + if (isset($params['durationSeconds'])) { + if (is_int($params['durationSeconds'])) { + $this->durationSeconds = $params['durationSeconds']; + } + } + if ($this->durationSeconds < 900) { + throw new InvalidArgumentException('Role session expiration should be in the range of 900s - max session duration'); + } + } + + private function filterSTSEndpoint(array $params) + { + if (isset($params['stsEndpoint'])) { + $this->stsEndpoint = $params['stsEndpoint']; + } + + if (is_null($this->stsEndpoint) || $this->stsEndpoint === '') { + $this->stsEndpoint = 'sts.ap-northeast-1.aliyuncs.com'; + } + } + + + /** + * Get credentials by request. + * + * @return array + * @throws RuntimeException + * @throws GuzzleException + */ + public function refreshCredentials() + { + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $options['query']['Action'] = 'GenerateSessionAccessKey'; + $options['query']['Version'] = '2015-04-01'; + $options['query']['Format'] = 'JSON'; + $options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z'); + $options['query']['SignatureMethod'] = 'SHA256withRSA'; + $options['query']['SignatureType'] = 'PRIVATEKEY'; + $options['query']['SignatureVersion'] = '1.0'; + $options['query']['SignatureNonce'] = Request::uuid(json_encode($options['query'])); + $options['query']['DurationSeconds'] = (string) $this->durationSeconds; + $options['query']['AccessKeyId'] = $this->publicKeyId; + $options['query']['Signature'] = Request::shaHmac256WithRsasign( + Request::signString('GET', $options['query']), + $this->privateKey + ); + + $url = (new Uri())->withScheme('https')->withHost($this->stsEndpoint); + + $result = Request::createClient()->request('GET', $url, $options); + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error refreshing credentials from RsaKeyPair, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result); + } + + $json = $result->toArray(); + + if (!isset($json['SessionAccessKey']['SessionAccessKeyId']) || !isset($json['SessionAccessKey']['SessionAccessKeySecret'])) { + throw new RuntimeException('Error retrieving credentials from RsaKeyPair result:' . $result->toJson()); + } + + $credentials = []; + $credentials['AccessKeyId'] = $json['SessionAccessKey']['SessionAccessKeyId']; + $credentials['AccessKeySecret'] = $json['SessionAccessKey']['SessionAccessKeySecret']; + $credentials['Expiration'] = $json['SessionAccessKey']['Expiration']; + $credentials['SecurityToken'] = null; + + + return $credentials; + } + + public function key() + { + return 'rsa_key_pair#publicKeyId#' . $this->publicKeyId; + } + + public function getProviderName() + { + return 'rsa_key_pair'; + } + + /** + * @return string + */ + public function getPublicKeyId() + { + return $this->publicKeyId; + } + + /** + * @return mixed + */ + public function getPrivateKey() + { + return $this->privateKey; + } +} diff --git a/src/Providers/RsaKeyPairProvider.php b/src/Providers/RsaKeyPairProvider.php deleted file mode 100644 index c2d03fc..0000000 --- a/src/Providers/RsaKeyPairProvider.php +++ /dev/null @@ -1,53 +0,0 @@ -getCredentialsInCache(); - - if ($credential === null) { - $result = (new GenerateSessionAccessKey($this->credential))->request(); - - if ($result->getStatusCode() !== 200) { - throw new RuntimeException(isset($result['Message']) ? $result['Message'] : (string)$result->getBody()); - } - - if (!isset($result['SessionAccessKey']['SessionAccessKeyId'], - $result['SessionAccessKey']['SessionAccessKeySecret'])) { - throw new RuntimeException($this->error); - } - - $credential = $result['SessionAccessKey']; - $this->cache($credential); - } - - return new StsCredential( - $credential['SessionAccessKeyId'], - $credential['SessionAccessKeySecret'], - strtotime($credential['Expiration']) - ); - } -} diff --git a/src/Providers/SessionCredentialsProvider.php b/src/Providers/SessionCredentialsProvider.php new file mode 100644 index 0000000..21b10cc --- /dev/null +++ b/src/Providers/SessionCredentialsProvider.php @@ -0,0 +1,86 @@ +key()])) { + $result = self::$credentialsCache[$this->key()]; + if (\strtotime($result['Expiration']) - \time() >= $this->expirationSlot) { + return $result; + } + } + + return null; + } + + /** + * Cache credentials. + * + * @param array $credential + */ + protected function cache(array $credential) + { + self::$credentialsCache[$this->key()] = $credential; + } + + /** + * Get credential. + * + * @return Credentials + */ + public function getCredentials() + { + $credentials = $this->getCredentialsInCache(); + + if ($credentials === null) { + $credentials = $this->refreshCredentials(); + $this->cache($credentials); + } + + return new Credentials([ + 'accessKeyId' => $credentials['AccessKeyId'], + 'accessKeySecret' => $credentials['AccessKeySecret'], + 'securityToken' => $credentials['SecurityToken'], + 'expiration' => \strtotime($credentials['Expiration']), + 'providerName' => $this->getProviderName(), + ]); + } + + + /** + * @return array + */ + abstract function refreshCredentials(); + + /** + * Get the toString of the credentials provider as the key. + * + * @return string + */ + abstract function key(); +} diff --git a/src/Providers/StaticAKCredentialsProvider.php b/src/Providers/StaticAKCredentialsProvider.php new file mode 100644 index 0000000..7e73cd0 --- /dev/null +++ b/src/Providers/StaticAKCredentialsProvider.php @@ -0,0 +1,78 @@ +filterAK($params); + } + + private function filterAK(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID')) { + $this->accessKeyId = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_ID'); + } + + if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET')) { + $this->accessKeySecret = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_SECRET'); + } + + if (isset($params['accessKeyId'])) { + $this->accessKeyId = $params['accessKeyId']; + } + if (isset($params['accessKeySecret'])) { + $this->accessKeySecret = $params['accessKeySecret']; + } + + Filter::accessKey($this->accessKeyId, $this->accessKeySecret); + } + + /** + * Get credential. + * + * @return Credentials + */ + public function getCredentials() + { + return new Credentials([ + 'accessKeyId' => $this->accessKeyId, + 'accessKeySecret' => $this->accessKeySecret, + 'providerName' => $this->getProviderName(), + ]); + } + + /** + * @inheritDoc + */ + public function getProviderName() + { + return "static_ak"; + } +} \ No newline at end of file diff --git a/src/Providers/StaticSTSCredentialsProvider.php b/src/Providers/StaticSTSCredentialsProvider.php new file mode 100644 index 0000000..957b25d --- /dev/null +++ b/src/Providers/StaticSTSCredentialsProvider.php @@ -0,0 +1,92 @@ +filterSTS($params); + } + + private function filterSTS(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_ID')) { + $this->accessKeyId = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_ID'); + } + + if (Helper::envNotEmpty('ALIBABA_CLOUD_ACCESS_KEY_SECRET')) { + $this->accessKeySecret = Helper::env('ALIBABA_CLOUD_ACCESS_KEY_SECRET'); + } + + if (Helper::envNotEmpty('ALIBABA_CLOUD_SECURITY_TOKEN')) { + $this->securityToken = Helper::env('ALIBABA_CLOUD_SECURITY_TOKEN'); + } + + if (isset($params['accessKeyId'])) { + $this->accessKeyId = $params['accessKeyId']; + } + if (isset($params['accessKeySecret'])) { + $this->accessKeySecret = $params['accessKeySecret']; + } + if (isset($params['securityToken'])) { + $this->securityToken = $params['securityToken']; + } + + Filter::accessKey($this->accessKeyId, $this->accessKeySecret); + Filter::securityToken($this->securityToken); + } + + /** + * Get credential. + * + * @return Credentials + */ + public function getCredentials() + { + return new Credentials([ + 'accessKeyId' => $this->accessKeyId, + 'accessKeySecret' => $this->accessKeySecret, + 'securityToken' => $this->securityToken, + 'providerName' => $this->getProviderName(), + ]); + } + + /** + * @inheritDoc + */ + public function getProviderName() + { + return "static_sts"; + } +} \ No newline at end of file diff --git a/src/Providers/URLCredentialsProvider.php b/src/Providers/URLCredentialsProvider.php new file mode 100644 index 0000000..9b3dc2a --- /dev/null +++ b/src/Providers/URLCredentialsProvider.php @@ -0,0 +1,119 @@ +filterOptions($options); + $this->filterCredentialsURI($params); + } + + private function filterOptions(array $options) + { + if (isset($options['connectTimeout'])) { + $this->connectTimeout = $options['connectTimeout']; + } + + if (isset($options['readTimeout'])) { + $this->readTimeout = $options['readTimeout']; + } + + Filter::timeout($this->connectTimeout, $this->readTimeout); + } + + private function filterCredentialsURI(array $params) + { + if (Helper::envNotEmpty('ALIBABA_CLOUD_CREDENTIALS_URI')) { + $this->credentialsURI = Helper::env('ALIBABA_CLOUD_CREDENTIALS_URI'); + } + + if (isset($params['credentialsURI'])) { + $this->credentialsURI = $params['credentialsURI']; + } + + Filter::credentialsURI($this->credentialsURI); + } + + /** + * Get credentials by request. + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException + * @throws GuzzleException + */ + public function refreshCredentials() + { + $options = Request::commonOptions(); + $options['read_timeout'] = $this->readTimeout; + $options['connect_timeout'] = $this->connectTimeout; + + $result = Request::createClient()->request('GET', $this->credentialsURI, $options); + + if ($result->getStatusCode() !== 200) { + throw new RuntimeException('Error refreshing credentials from credentialsURI, statusCode: ' . $result->getStatusCode() . ', result: ' . (string) $result); + } + + $credentials = $result->toArray(); + + if (!isset($credentials['AccessKeyId']) || !isset($credentials['AccessKeySecret']) || !isset($credentials['SecurityToken']) || !isset($credentials['Expiration'])) { + throw new RuntimeException('Error retrieving credentials from credentialsURI result:' . $result->toJson()); + } + + return $credentials; + } + + + /** + * @return string + */ + public function key() + { + return 'credential_uri#' . $this->credentialsURI; + } + + /** + * @return string + */ + public function getProviderName() + { + return 'credential_uri'; + } +} diff --git a/src/RamRoleArnCredential.php b/src/RamRoleArnCredential.php index b82c608..ed75640 100644 --- a/src/RamRoleArnCredential.php +++ b/src/RamRoleArnCredential.php @@ -2,13 +2,16 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\RamRoleArnProvider; +use AlibabaCloud\Credentials\Providers\RamRoleArnCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Utils\Filter; use Exception; use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; /** + * @deprecated * Use the AssumeRole of the RAM account to complete the authentication. */ class RamRoleArnCredential implements CredentialsInterface @@ -57,10 +60,10 @@ public function __construct(array $credential = [], array $config = []) Filter::accessKey($credential['access_key_id'], $credential['access_key_secret']); - $this->config = $config; - $this->accessKeyId = $credential['access_key_id']; + $this->config = $config; + $this->accessKeyId = $credential['access_key_id']; $this->accessKeySecret = $credential['access_key_secret']; - $this->roleArn = $credential['role_arn']; + $this->roleArn = $credential['role_arn']; $this->roleSessionName = $credential['role_session_name']; } @@ -177,13 +180,20 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return AlibabaCloud\Credentials\Providers\Credentials * @throws Exception * @throws GuzzleException */ protected function getSessionCredential() { - return (new RamRoleArnProvider($this))->get(); + $params = [ + 'accessKeyId' => $this->accessKeyId, + 'accessKeySecret' => $this->accessKeyId, + 'roleArn' => $this->roleArn, + 'roleSessionName' => $this->roleSessionName, + 'policy' => $this->policy, + ]; + return (new RamRoleArnCredentialsProvider($params))->getCredentials(); } /** @@ -215,4 +225,18 @@ public function getExpiration() { return $this->getSessionCredential()->getExpiration(); } + + /** + * @inheritDoc + */ + public function getCredential() + { + $credentials = $this->getSessionCredential(); + return new CredentialModel([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'type' => 'ram_role_arn', + ]); + } } diff --git a/src/Request/AssumeRole.php b/src/Request/AssumeRole.php deleted file mode 100644 index 962a733..0000000 --- a/src/Request/AssumeRole.php +++ /dev/null @@ -1,37 +0,0 @@ -signature = new ShaHmac1Signature(); - $this->credential = $arnCredential; - $this->uri = $this->uri->withHost('sts.aliyuncs.com'); - $this->options['verify'] = false; - $this->options['query']['RoleArn'] = $arnCredential->getRoleArn(); - $this->options['query']['RoleSessionName'] = $arnCredential->getRoleSessionName(); - $this->options['query']['DurationSeconds'] = Provider::DURATION_SECONDS; - $this->options['query']['AccessKeyId'] = $this->credential->getOriginalAccessKeyId(); - $this->options['query']['Version'] = '2015-04-01'; - $this->options['query']['Action'] = 'AssumeRole'; - $this->options['query']['RegionId'] = 'cn-hangzhou'; - if ($arnCredential->getPolicy()) { - $this->options['query']['Policy'] = $arnCredential->getPolicy(); - } - } -} diff --git a/src/Request/GenerateSessionAccessKey.php b/src/Request/GenerateSessionAccessKey.php deleted file mode 100644 index 35db585..0000000 --- a/src/Request/GenerateSessionAccessKey.php +++ /dev/null @@ -1,33 +0,0 @@ -signature = new ShaHmac256WithRsaSignature(); - $this->credential = $credential; - $this->uri = $this->uri->withHost('sts.ap-northeast-1.aliyuncs.com'); - $this->options['verify'] = false; - $this->options['query']['Version'] = '2015-04-01'; - $this->options['query']['Action'] = 'GenerateSessionAccessKey'; - $this->options['query']['RegionId'] = 'cn-hangzhou'; - $this->options['query']['AccessKeyId'] = $credential->getPublicKeyId(); - $this->options['query']['PublicKeyId'] = $credential->getPublicKeyId(); - $this->options['query']['DurationSeconds'] = Provider::DURATION_SECONDS; - } -} diff --git a/src/Request/Request.php b/src/Request/Request.php index 6945e1b..f0ba62f 100644 --- a/src/Request/Request.php +++ b/src/Request/Request.php @@ -3,19 +3,16 @@ namespace AlibabaCloud\Credentials\Request; use AlibabaCloud\Credentials\Credentials; -use AlibabaCloud\Credentials\EcsRamRoleCredential; -use AlibabaCloud\Credentials\Helper; -use AlibabaCloud\Credentials\RamRoleArnCredential; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; -use AlibabaCloud\Credentials\Signature\ShaHmac256WithRsaSignature; -use Exception; +use AlibabaCloud\Credentials\Utils\Helper; use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; -use GuzzleHttp\Psr7\Uri; use AlibabaCloud\Tea\Response; use Psr\Http\Message\ResponseInterface; +use Exception; +use InvalidArgumentException; + /** * RESTful RPC Request. */ @@ -28,67 +25,33 @@ class Request const CONNECT_TIMEOUT = 5; /** - * Request Timeout + * Request Read Timeout */ - const TIMEOUT = 10; + const READ_TIMEOUT = 5; /** * @var array */ private static $config = []; - /** - * @var array - */ - public $options = []; - - /** - * @var Uri - */ - public $uri; - - /** - * @var EcsRamRoleCredential|RamRoleArnCredential - */ - protected $credential; - - /** - * @var ShaHmac256WithRsaSignature|ShaHmac1Signature - */ - protected $signature; /** - * Request constructor. + * + * @return array */ - public function __construct() + public static function commonOptions() { - $this->uri = (new Uri())->withScheme('https'); - $this->options['http_errors'] = false; - $this->options['connect_timeout'] = self::CONNECT_TIMEOUT; - $this->options['timeout'] = self::TIMEOUT; + $options = []; + $options['http_errors'] = false; + $options['connect_timeout'] = self::CONNECT_TIMEOUT; + $options['read_timeout'] = self::READ_TIMEOUT; + $options['headers']['User-Agent'] = Helper::getUserAgent(); // Turn on debug mode based on environment variable. if (strtolower(Helper::env('DEBUG')) === 'sdk') { - $this->options['debug'] = true; + $options['debug'] = true; } - } - - /** - * @return ResponseInterface - * @throws Exception - */ - public function request() - { - $this->options['query']['Format'] = 'JSON'; - $this->options['query']['SignatureMethod'] = $this->signature->getMethod(); - $this->options['query']['SignatureVersion'] = $this->signature->getVersion(); - $this->options['query']['SignatureNonce'] = self::uuid(json_encode($this->options['query'])); - $this->options['query']['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z'); - $this->options['query']['Signature'] = $this->signature->sign( - self::signString('GET', $this->options['query']), - $this->credential->getOriginalAccessKeySecret() . '&' - ); - return self::createClient()->request('GET', (string)$this->uri, $this->options); + return $options; } /** @@ -118,6 +81,53 @@ public static function signString($method, array $parameters) return $method . '&%2F&' . self::percentEncode(substr($canonicalized, 1)); } + /** + * @param string $string + * @param string $accessKeySecret + * + * @return string + */ + public static function shaHmac1sign($string, $accessKeySecret) + { + return base64_encode(hash_hmac('sha1', $string, $accessKeySecret, true)); + } + + /** + * @param string $string + * @param string $accessKeySecret + * + * @return string + */ + public static function shaHmac256sign($string, $accessKeySecret) + { + return base64_encode(hash_hmac('sha256', $string, $accessKeySecret, true)); + } + + /** + * @param string $string + * @param string $privateKey + * + * @return string + */ + public static function shaHmac256WithRsasign($string, $privateKey) + { + $binarySignature = ''; + try { + openssl_sign( + $string, + $binarySignature, + $privateKey, + \OPENSSL_ALGO_SHA256 + ); + } catch (Exception $exception) { + throw new InvalidArgumentException( + $exception->getMessage() + ); + } + + return base64_encode($binarySignature); + } + /** * @param string $string * diff --git a/src/RsaKeyPairCredential.php b/src/RsaKeyPairCredential.php index bb47f6b..12e719e 100644 --- a/src/RsaKeyPairCredential.php +++ b/src/RsaKeyPairCredential.php @@ -2,13 +2,16 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\RsaKeyPairProvider; +use AlibabaCloud\Credentials\Providers\RsaKeyPairCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Utils\Filter; use Exception; use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; /** + * @deprecated * Use the RSA key pair to complete the authentication (supported only on Japanese site) */ class RsaKeyPairCredential implements CredentialsInterface @@ -19,6 +22,11 @@ class RsaKeyPairCredential implements CredentialsInterface */ private $publicKeyId; + /** + * @var string + */ + private $privateKeyFile; + /** * @var string */ @@ -42,7 +50,8 @@ public function __construct($public_key_id, $private_key_file, array $config = [ Filter::privateKeyFile($private_key_file); $this->publicKeyId = $public_key_id; - $this->config = $config; + $this->privateKeyFile = $private_key_file; + $this->config = $config; try { $this->privateKey = file_get_contents($private_key_file); } catch (Exception $exception) { @@ -117,13 +126,17 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return AlibabaCloud\Credentials\Providers\Credentials * @throws Exception * @throws GuzzleException */ protected function getSessionCredential() { - return (new RsaKeyPairProvider($this))->get(); + $params = [ + 'publicKeyId' => $this->publicKeyId, + 'privateKeyFile' => $this->privateKeyFile, + ]; + return (new RsaKeyPairCredentialsProvider($params))->getCredentials(); } /** @@ -155,4 +168,18 @@ public function getExpiration() { return $this->getSessionCredential()->getExpiration(); } + + /** + * @inheritDoc + */ + public function getCredential() + { + $credentials = $this->getSessionCredential(); + return new CredentialModel([ + 'accessKeyId' => $credentials->getAccessKeyId(), + 'accessKeySecret' => $credentials->getAccessKeySecret(), + 'securityToken' => $credentials->getSecurityToken(), + 'type' => 'rsa_key_pair', + ]); + } } diff --git a/src/StsCredential.php b/src/StsCredential.php index 89a3a84..709a8ee 100644 --- a/src/StsCredential.php +++ b/src/StsCredential.php @@ -2,9 +2,12 @@ namespace AlibabaCloud\Credentials; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; /** + * @deprecated * Use the STS Token to complete the authentication. */ class StsCredential implements CredentialsInterface @@ -42,10 +45,10 @@ public function __construct($access_key_id, $access_key_secret, $expiration, $se { Filter::accessKey($access_key_id, $access_key_secret); Filter::expiration($expiration); - $this->accessKeyId = $access_key_id; + $this->accessKeyId = $access_key_id; $this->accessKeySecret = $access_key_secret; - $this->expiration = $expiration; - $this->securityToken = $security_token; + $this->expiration = $expiration; + $this->securityToken = $security_token; } /** @@ -95,4 +98,18 @@ public function getSignature() { return new ShaHmac1Signature(); } + + /** + * @inheritDoc + */ + public function getCredential() + { + return new CredentialModel([ + 'accessKeyId' => $this->accessKeyId, + 'accessKeySecret' => $this->accessKeySecret, + 'securityToken' => $this->securityToken, + 'type' => 'sts', + ]); + } + } diff --git a/src/Utils/Filter.php b/src/Utils/Filter.php new file mode 100644 index 0000000..959fd8f --- /dev/null +++ b/src/Utils/Filter.php @@ -0,0 +1,233 @@ + 'access_key', - 'accessKeyId' => 'foo', - 'accessKeySecret' => 'bar', - ]); - $credential = new Credential($config); - - // Assert - $this->assertEquals('foo', $credential->getAccessKeyId()); - $this->assertEquals('bar', $credential->getAccessKeySecret()); - $this->assertEquals('access_key', $credential->getType()); - } - /** * @throws GuzzleException * @throws ReflectionException @@ -54,15 +35,15 @@ public function testEcsRamRoleCredential() $this->expectException(\GuzzleHttp\Exception\ConnectException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/timed/'); + $this->expectExceptionMessageMatches('/Connection timeout/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/timed/'); + $this->expectExceptionMessageRegExp('/Connection timeout/'); } // Assert $this->assertEquals('foo', $credential->getRoleName()); $this->assertEquals('ecs_ram_role', $credential->getType()); - $credential->getAccessKeySecret(); + $credential->getCredential(); } /** @@ -84,19 +65,25 @@ public function testRamRoleArnCredential() $credential = new Credential($config); // Assert - $this->assertTrue(null !== $credential->getAccessKeyId()); - $this->assertTrue(null !== $credential->getAccessKeySecret()); - $this->assertEquals('ram_role_arn', $credential->getType()); + $result = $credential->getCredential(); + $this->assertNotNull($result->getAccessKeyId()); + $this->assertNotNull($result->getAccessKeySecret()); + $this->assertEquals('ram_role_arn', $result->getType()); } /** * @throws GuzzleException * @throws ReflectionException - * @expectedException \RuntimeException - * @expectedExceptionMessage Specified access key type is not match with signature type. */ public function testRsaKeyPairCredential() { + $this->expectException(RuntimeException::class); + $reg = '/Error refreshing credentials from RsaKeyPair, statusCode: 404/'; + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches($reg); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp($reg); + } Credentials::cancelMock(); $publicKeyId = Helper::envNotEmpty('PUBLIC_KEY_ID'); $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); @@ -106,51 +93,40 @@ public function testRsaKeyPairCredential() 'privateKeyFile' => $privateKeyFile, ]); $credential = new Credential($config); - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Specified access key type is not match with signature type.'); // Assert - $this->assertTrue(null !== $credential->getAccessKeyId()); - $this->assertTrue(null !== $credential->getAccessKeySecret()); - $this->assertEquals('rsa_key_pair', $credential->getType()); - - $credential->getAccessKeySecret(); + $result = $credential->getCredential(); + $this->assertNotNull($result->getAccessKeyId()); + $this->assertNotNull($result->getAccessKeySecret()); + $this->assertEquals('rsa_key_pair', $result->getType()); } /** * @throws GuzzleException * @throws ReflectionException */ - public function testSTS() + public function testOIDCRoleArnCredential() { - $config = new Credential\Config([ - 'type' => 'sts', - 'accessKeyId' => 'foo', - 'accessKeySecret' => 'bar', - 'securityToken' => 'token', - ]); - $credential = new Credential($config); + Credentials::cancelMock(); + $credential = new Credential(); - // Assert - $this->assertEquals('foo', $credential->getAccessKeyId()); - $this->assertEquals('bar', $credential->getAccessKeySecret()); - $this->assertEquals('token', $credential->getSecurityToken()); - $this->assertEquals('sts', $credential->getType()); - } + $result = $credential->getCredential(); + $this->assertNotNull($result->getAccessKeyId()); + $this->assertNotNull($result->getAccessKeySecret()); + $this->assertNotNull($result->getSecurityToken()); + $this->assertEquals('default', $result->getType()); - /** - * @throws GuzzleException - * @throws ReflectionException - */ - public function testBearerToken() - { - $config = new Credential\Config([ - 'type' => 'bearer', - 'bearerToken' => 'token', + $config = new Credential\Config([ + 'type' => 'oidc_role_arn', + 'roleSessionName' => 'github_php_oidc_role_session_name', ]); + $credential = new Credential($config); - // Assert - $this->assertEquals('token', $credential->getBearerToken()); - $this->assertEquals('bearer', $credential->getType()); + $result = $credential->getCredential(); + $this->assertNotNull($result->getAccessKeyId()); + $this->assertNotNull($result->getAccessKeySecret()); + $this->assertNotNull($result->getSecurityToken()); + $this->assertEquals('oidc_role_arn', $result->getType()); } + } diff --git a/tests/Mock/VirtualFile.php b/tests/Mock/VirtualFile.php index ac66bf6..86e95da 100644 --- a/tests/Mock/VirtualFile.php +++ b/tests/Mock/VirtualFile.php @@ -17,7 +17,7 @@ class VirtualFile * * @param string $content */ - private function __construct($content) + public function __construct($content) { $this->content = $content; } diff --git a/tests/Unit/AccessKeyCredentialTest.php b/tests/Unit/AccessKeyCredentialTest.php index 07dad10..f5637d4 100644 --- a/tests/Unit/AccessKeyCredentialTest.php +++ b/tests/Unit/AccessKeyCredentialTest.php @@ -26,13 +26,19 @@ public function testConstruct() // Assert $this->assertEquals($accessKeyId, $credential->getAccessKeyId()); $this->assertEquals($accessKeySecret, $credential->getAccessKeySecret()); + $this->assertEquals('', $credential->getSecurityToken()); $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals("$accessKeyId#$accessKeySecret", (string)$credential); + + $credentialModel = $credential->getCredential(); + $this->assertEquals($accessKeyId, $credentialModel->getAccessKeyId()); + $this->assertEquals($accessKeySecret, $credentialModel->getAccessKeySecret()); + $this->assertEquals('access_key', $credentialModel->getType()); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage access_key_id cannot be empty + * @expectedExceptionMessage accessKeyId cannot be empty */ public function testAccessKeyIdEmpty() { @@ -41,14 +47,14 @@ public function testAccessKeyIdEmpty() $accessKeySecret = 'bar'; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_id cannot be empty'); + $this->expectExceptionMessage('accessKeyId cannot be empty'); new AccessKeyCredential($accessKeyId, $accessKeySecret); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage access_key_id must be a string + * @expectedExceptionMessage accessKeyId must be a string */ public function testAccessKeyIdFormat() { @@ -57,14 +63,14 @@ public function testAccessKeyIdFormat() $accessKeySecret = 'bar'; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_id must be a string'); + $this->expectExceptionMessage('accessKeyId must be a string'); new AccessKeyCredential($accessKeyId, $accessKeySecret); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage access_key_secret cannot be empty + * @expectedExceptionMessage accessKeySecret cannot be empty */ public function testAccessKeySecretEmpty() { @@ -73,7 +79,7 @@ public function testAccessKeySecretEmpty() $accessKeySecret = ''; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_secret cannot be empty'); + $this->expectExceptionMessage('accessKeySecret cannot be empty'); // Test new AccessKeyCredential($accessKeyId, $accessKeySecret); @@ -81,7 +87,7 @@ public function testAccessKeySecretEmpty() /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage access_key_secret must be a string + * @expectedExceptionMessage accessKeySecret must be a string */ public function testAccessKeySecretFormat() { @@ -90,7 +96,7 @@ public function testAccessKeySecretFormat() $accessKeySecret = null; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_secret must be a string'); + $this->expectExceptionMessage('accessKeySecret must be a string'); // Test new AccessKeyCredential($accessKeyId, $accessKeySecret); diff --git a/tests/Unit/BearerTokenCredentialTest.php b/tests/Unit/BearerTokenCredentialTest.php index a3f6b5b..34a5fc3 100644 --- a/tests/Unit/BearerTokenCredentialTest.php +++ b/tests/Unit/BearerTokenCredentialTest.php @@ -18,7 +18,7 @@ class BearerTokenCredentialTest extends TestCase /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage bearer_token cannot be empty + * @expectedExceptionMessage bearerToken cannot be empty */ public function testBearerTokenEmpty() { @@ -26,7 +26,7 @@ public function testBearerTokenEmpty() $bearerToken = ''; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('bearer_token cannot be empty'); + $this->expectExceptionMessage('bearerToken cannot be empty'); // Test new BearerTokenCredential($bearerToken); @@ -34,7 +34,7 @@ public function testBearerTokenEmpty() /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage bearer_token must be a string + * @expectedExceptionMessage bearerToken must be a string */ public function testBearerTokenFormat() { @@ -42,7 +42,7 @@ public function testBearerTokenFormat() $bearerToken = null; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('bearer_token must be a string'); + $this->expectExceptionMessage('bearerToken must be a string'); // Test new BearerTokenCredential($bearerToken); @@ -61,5 +61,9 @@ public function testConstruct() $this->assertEquals($bearerToken, $credential->getBearerToken()); $this->assertEquals($expected, (string)$credential); $this->assertInstanceOf(BearerTokenSignature::class, $credential->getSignature()); + + $credentialModel = $credential->getCredential(); + $this->assertEquals($bearerToken, $credentialModel->getBearerToken()); + $this->assertEquals('bearer', $credentialModel->getType()); } } diff --git a/tests/Unit/ChainProviderTest.php b/tests/Unit/ChainProviderTest.php index ca72caf..673f285 100644 --- a/tests/Unit/ChainProviderTest.php +++ b/tests/Unit/ChainProviderTest.php @@ -1,8 +1,8 @@ expectException(RuntimeException::class); $this->expectExceptionMessage('Credentials file is not readable: /a/c'); ChainProvider::customProvider(ChainProvider::getDefaultName()); + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE='); } public function testInOpenBaseDir() { if (!Helper::isWindows()) { - $dirs = 'vfs://AlibabaCloud:/home:/Users:/private:/a/b:/d'; + $dirs = 'vfs://AlibabaCloud:/home:/Users:/private:/a/b'; } else { $dirs = 'C:\\projects;C:\\Users'; } @@ -84,6 +106,8 @@ public function testInOpenBaseDir() ); self::assertTrue(ChainProvider::hasCustomChain()); ChainProvider::customProvider(ChainProvider::getDefaultName()); + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE='); + ini_set('open_basedir', null); } public function testDefaultProvider() @@ -108,6 +132,7 @@ public function testSetInstance() ); self::assertTrue(ChainProvider::hasCustomChain()); ChainProvider::customProvider(ChainProvider::getDefaultName()); + putenv('ALIBABA_CLOUD_ECS_METADATA='); } public function testDefaultFile() @@ -116,7 +141,7 @@ public function testDefaultFile() 'credentials', ChainProvider::getDefaultFile() ); - putenv('ALIBABA_CLOUD_PROFILE=default'); + putenv('ALIBABA_CLOUD_PROFILE='); } public function testDefaultName() @@ -132,15 +157,7 @@ public function testDefaultName() 'default', ChainProvider::getDefaultName() ); - } - /** - * @before - */ - protected function initialize() - { - parent::setUp(); - putenv('ALIBABA_CLOUD_ACCESS_KEY_ID=foo'); - putenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET=bar'); + putenv('ALIBABA_CLOUD_PROFILE='); } } diff --git a/tests/Unit/CredentialTest.php b/tests/Unit/CredentialTest.php index f5a2d55..feee2e4 100644 --- a/tests/Unit/CredentialTest.php +++ b/tests/Unit/CredentialTest.php @@ -1,10 +1,10 @@ getMessage(), "Credential 'default' not found"); - } + new Credential(); self::assertTrue(true); } @@ -53,21 +49,21 @@ public function exceptionCases() [ 'access_key_id' => 'access_key_id', ], - 'Missing required type option', + 'Unsupported credential type option: default, support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri', ], [ [ 'type' => 'none', ], - 'Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair, bearer', + 'Unsupported credential type option: none, support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri', ], [ [ 'type' => 'access_key', ], - 'Missing required access_key_id option in config for access_key', + 'accessKeyId must be a string', ], [ @@ -75,7 +71,7 @@ public function exceptionCases() 'type' => 'access_key', 'access_key_id' => 'foo', ], - 'Missing required access_key_secret option in config for access_key', + 'accessKeySecret must be a string', ], [ @@ -84,7 +80,7 @@ public function exceptionCases() 'access_key_id' => '', 'access_key_secret' => 'bar', ], - 'access_key_id cannot be empty', + 'accessKeyId cannot be empty', ], [ @@ -93,14 +89,14 @@ public function exceptionCases() 'access_key_id' => 'foo', 'access_key_secret' => '', ], - 'access_key_secret cannot be empty', + 'accessKeySecret cannot be empty', ], [ [ 'type' => 'sts', ], - 'Missing required access_key_id option in config for sts', + 'accessKeyId must be a string', ], [ @@ -108,7 +104,7 @@ public function exceptionCases() 'type' => 'sts', 'access_key_id' => 'foo', ], - 'Missing required access_key_secret option in config for sts', + 'accessKeySecret must be a string', ], [ @@ -117,7 +113,7 @@ public function exceptionCases() 'access_key_id' => 'foo', 'access_key_secret' => 'bar', ], - 'Missing required expiration option in config for sts', + 'securityToken must be a string', ], [ @@ -127,7 +123,7 @@ public function exceptionCases() 'access_key_secret' => 'bar', 'expiration' => 3600, ], - 'access_key_id cannot be empty', + 'accessKeyId cannot be empty', ], [ @@ -137,7 +133,7 @@ public function exceptionCases() 'access_key_secret' => '', 'expiration' => 3600, ], - 'access_key_secret cannot be empty', + 'accessKeySecret cannot be empty', ], [ @@ -147,118 +143,383 @@ public function exceptionCases() 'access_key_secret' => 'bar', 'expiration' => 'string', ], - 'expiration must be a int', + 'securityToken must be a string', ], [ [ - 'type' => 'ecs_ram_role', + 'type' => 'ecs_ram_role', + 'role_name' => 123456, ], - 'Missing required role_name option in config for ecs_ram_role', + 'roleName must be a string', ], [ [ 'type' => 'ecs_ram_role', - 'role_name' => '', + 'role_name' => 'test', + 'disableIMDSv1' => 'false', ], - 'role_name cannot be empty', + 'disableIMDSv1 must be a boolean', ], [ [ - 'type' => 'ecs_ram_role', - 'role_name' => 123456, + 'type' => 'ram_role_arn', + ], + 'accessKeyId must be a string', + ], + + [ + [ + 'type' => 'ram_role_arn', + 'access_key_id' => 'foo', + ], + 'accessKeySecret must be a string', + ], + + [ + [ + 'type' => 'ram_role_arn', + 'access_key_id' => 'foo', + 'access_key_secret' => 'bar', + ], + 'roleArn cannot be empty', + ], + + [ + [ + 'type' => 'ram_role_arn', + 'access_key_id' => 'foo', + 'access_key_secret' => 'bar', + 'security_token' => 'token', + ], + 'roleArn cannot be empty', + ], + + [ + [ + 'type' => 'rsa_key_pair', + ], + 'publicKeyId must be a string', + ], + + [ + [ + 'type' => 'rsa_key_pair', + 'public_key_id' => 'public_key_id', + ], + 'privateKeyFile must be a string', + ], + + [ + [ + 'type' => 'rsa_key_pair', + 'public_key_id' => 'public_key_id', + 'private_key_file' => '', + ], + 'privateKeyFile cannot be empty', + ], + + [ + [ + 'type' => 'rsa_key_pair', + 'public_key_id' => 'public_key_id', + 'private_key_file' => 'invalid_path', + ], + 'file_get_contents(invalid_path): failed to open stream: No such file or directory', + ], + + [ + [ + 'type' => 'oidc_role_arn', + ], + 'roleArn cannot be empty', + ], + + [ + [ + 'type' => 'oidc_role_arn', + 'role_arn' => 'role_arn', + ], + 'oidcProviderArn cannot be empty', + ], + + [ + [ + 'type' => 'oidc_role_arn', + 'role_arn' => 'role_arn', + 'oidc_provider_arn' => 'oidc_provider_arn', + ], + 'oidcTokenFilePath cannot be empty', + ], + + [ + [ + 'type' => 'credentials_uri', + ], + 'credentialsURI must be a string', + ], + + [ + [ + 'type' => 'credentials_uri', + 'credentialsURI' => '' + ], + 'credentialsURI must be a string', + ], + + ]; + } + + /** + * @dataProvider exceptionConfigCases + * + * @param array $config + * @param string $message + */ + public function testConfigException(array $map, $message) + { + try { + $config = new Config($map); + new Credential($config); + } catch (Exception $e) { + self::assertEquals(strtolower($message), strtolower($e->getMessage())); + } + self::assertTrue(true); + } + + /** + * @return array + */ + public function exceptionConfigCases() + { + return [ + + [ + [ + 'accessKeyId' => 'access_key_id', + ], + 'Unsupported credential type option: default, support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri', + ], + + [ + [ + 'type' => 'none', ], - 'role_name must be a string', + 'Unsupported credential type option: none, support: access_key, sts, bearer, ecs_ram_role, ram_role_arn, rsa_key_pair, oidc_role_arn, credentials_uri', + ], + + [ + [ + 'type' => 'access_key', + ], + 'accessKeyId must be a string', + ], + + [ + [ + 'type' => 'access_key', + 'accessKeyId' => 'foo', + ], + 'accessKeySecret must be a string', + ], + + [ + [ + 'type' => 'access_key', + 'accessKeyId' => '', + 'accessKeySecret' => 'bar', + ], + 'accessKeyId cannot be empty', + ], + + [ + [ + 'type' => 'access_key', + 'accessKeyId' => 'foo', + 'accessKeySecret' => '', + ], + 'accessKeySecret cannot be empty', + ], + + [ + [ + 'type' => 'sts', + ], + 'accessKeyId must be a string', + ], + + [ + [ + 'type' => 'sts', + 'accessKeyId' => 'foo', + ], + 'accessKeySecret must be a string', + ], + + [ + [ + 'type' => 'sts', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + ], + 'securityToken must be a string', + ], + + [ + [ + 'type' => 'sts', + 'accessKeyId' => '', + 'accessKeySecret' => 'bar', + 'expiration' => 3600, + ], + 'accessKeyId cannot be empty', + ], + + [ + [ + 'type' => 'sts', + 'accessKeyId' => 'foo', + 'accessKeySecret' => '', + 'expiration' => 3600, + ], + 'accessKeySecret cannot be empty', + ], + + [ + [ + 'type' => 'sts', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'expiration' => 'string', + ], + 'securityToken must be a string', ], [ [ 'type' => 'ecs_ram_role', - 'role_name' => 'test', - 'disableIMDSv1' => 'false', + 'roleName' => 123456, ], - 'disable_IMDS_v1 must be a boolean', + 'roleName must be a string', ], [ [ 'type' => 'ecs_ram_role', - 'role_name' => 'test', - 'disableIMDSv1' => false, - 'metadataTokenDuration' => 3600, + 'roleName' => 'test', + 'disableIMDSv1' => 'false', ], - 'metadata_token_duration must be a int', + 'disableIMDSv1 must be a boolean', ], [ [ 'type' => 'ram_role_arn', ], - 'Missing required access_key_id option in config for ram_role_arn', + 'accessKeyId must be a string', ], [ [ 'type' => 'ram_role_arn', - 'access_key_id' => 'foo', + 'accessKeyId' => 'foo', ], - 'Missing required access_key_secret option in config for ram_role_arn', + 'accessKeySecret must be a string', ], [ [ 'type' => 'ram_role_arn', - 'access_key_id' => 'foo', - 'access_key_secret' => 'bar', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', ], - 'Missing required role_arn option in config for ram_role_arn', + 'roleArn cannot be empty', ], [ [ 'type' => 'ram_role_arn', - 'access_key_id' => 'foo', - 'access_key_secret' => 'bar', - 'role_arn' => 'role_arn', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'securityToken' => 'token', ], - 'Missing required role_session_name option in config for ram_role_arn', + 'roleArn cannot be empty', ], [ [ 'type' => 'rsa_key_pair', ], - 'Missing required public_key_id option in config for rsa_key_pair', + 'publicKeyId must be a string', ], [ [ 'type' => 'rsa_key_pair', - 'public_key_id' => 'public_key_id', + 'publicKeyId' => 'public_key_id', ], - 'Missing required private_key_file option in config for rsa_key_pair', + 'privateKeyFile must be a string', ], [ [ 'type' => 'rsa_key_pair', - 'public_key_id' => 'public_key_id', - 'private_key_file' => '', + 'publicKeyId' => 'public_key_id', + 'privateKeyFile' => '', ], - 'private_key_file cannot be empty', + 'privateKeyFile cannot be empty', ], [ [ 'type' => 'rsa_key_pair', - 'public_key_id' => 'public_key_id', - 'private_key_file' => 'invalid_path', + 'publicKeyId' => 'public_key_id', + 'privateKeyFile' => 'invalid_path', ], 'file_get_contents(invalid_path): failed to open stream: No such file or directory', ], + [ + [ + 'type' => 'oidc_role_arn', + ], + 'roleArn cannot be empty', + ], + + [ + [ + 'type' => 'oidc_role_arn', + 'roleArn' => 'role_arn', + ], + 'oidcProviderArn cannot be empty', + ], + + [ + [ + 'type' => 'oidc_role_arn', + 'roleArn' => 'role_arn', + 'oidcProviderArn' => 'oidc_provider_arn', + ], + 'oidcTokenFilePath cannot be empty', + ], + + [ + [ + 'type' => 'credentials_uri', + ], + 'credentialsURI must be a string', + ], + + [ + [ + 'type' => 'credentials_uri', + 'credentialsURI' => '' + ], + 'credentialsURI cannot be empty', + ], + ]; } @@ -278,10 +539,37 @@ public function testRamRoleArnCredential() ]; $credential = new Credential($config); + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "********************", + "AccessKeyId": "STS.**************", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "**************" + } +}'; + Credentials::mockResponse(200, [], $result); + self::assertEquals('foo', $credential->getOriginalAccessKeyId()); self::assertEquals('bar', $credential->getOriginalAccessKeySecret()); - self::assertEquals($config, $credential->getConfig()); - self::assertInstanceOf(RamRoleArnCredential::class, $credential->getCredential()); + self::assertEquals([ + 'type' => 'ram_role_arn', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'roleArn' => 'role_arn', + 'roleSessionName' => 'role_session_name', + ], $credential->getConfig()); + self::assertEquals("STS.**************", $credential->getAccessKeyId()); + self::assertEquals("********************", $credential->getAccessKeySecret()); + self::assertEquals("**************", $credential->getSecurityToken()); + self::assertEquals("", $credential->getBearerToken()); + self::assertEquals("ram_role_arn", $credential->getType()); + $credentialModel = $credential->getCredential(); + self::assertEquals("STS.**************", $credentialModel->getAccessKeyId()); } /** @@ -300,7 +588,55 @@ public function testAccessKey() self::assertEquals('foo', $credential->getAccessKeyId()); self::assertEquals('bar', $credential->getAccessKeySecret()); - self::assertEquals($config, $credential->getConfig()); - self::assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); + self::assertEquals("", $credential->getSecurityToken()); + self::assertEquals("", $credential->getBearerToken()); + self::assertEquals("access_key", $credential->getType()); + $config = $credential->getConfig(); + self::assertEquals('foo', $config['accessKeyId']); + self::assertEquals('bar', $config['accessKeySecret']); + $result = $credential->getCredential(); + self::assertEquals('foo', $result->getAccessKeyId()); + self::assertEquals('bar', $result->getAccessKeySecret()); + self::assertEquals('access_key', $result->getType()); + } + + /** + * @throws GuzzleException + * @throws ReflectionException + */ + public function testSTS() + { + $config = new Credential\Config([ + 'type' => 'sts', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'securityToken' => 'token', + ]); + $credential = new Credential($config); + + // Assert + $result = $credential->getCredential(); + $this->assertEquals('foo', $result->getAccessKeyId()); + $this->assertEquals('bar', $result->getAccessKeySecret()); + $this->assertEquals('token', $result->getSecurityToken()); + $this->assertEquals('sts', $result->getType()); + } + + /** + * @throws GuzzleException + * @throws ReflectionException + */ + public function testBearerToken() + { + $config = new Credential\Config([ + 'type' => 'bearer', + 'bearerToken' => 'token', + ]); + $credential = new Credential($config); + + // Assert + $result = $credential->getCredential(); + $this->assertEquals('token', $result->getBearerToken()); + $this->assertEquals('bearer', $result->getType()); } } diff --git a/tests/Unit/CredentialsTest.php b/tests/Unit/CredentialsTest.php index ff94133..6a28aa1 100644 --- a/tests/Unit/CredentialsTest.php +++ b/tests/Unit/CredentialsTest.php @@ -1,6 +1,6 @@ expectException(RuntimeException::class); + $this->expectExceptionMessage("Credential 'default' not found"); Credentials::get(ChainProvider::getDefaultName()); Credentials::flush(); diff --git a/tests/Unit/EcsRamRoleCredentialTest.php b/tests/Unit/EcsRamRoleCredentialTest.php index b163da9..84fb770 100644 --- a/tests/Unit/EcsRamRoleCredentialTest.php +++ b/tests/Unit/EcsRamRoleCredentialTest.php @@ -46,10 +46,16 @@ public function testConstruct() // Assert $this->assertEquals($roleName, $credential->getRoleName()); $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); + $this->assertEquals(false, $credential->isDisableIMDSv1()); $this->assertEquals($expected, (string)$credential); + + Credentials::mockResponse(200, [], 'RoleName'); + $this->credential = new EcsRamRoleCredential(); + self::assertEquals('RoleName', $this->credential->getRoleName()); } - private function getPrivateField($instance, $field) { + private function getPrivateField($instance, $field) + { $reflection = new ReflectionClass(EcsRamRoleCredential::class); $privateProperty = $reflection->getProperty($field); $privateProperty->setAccessible(true); @@ -81,21 +87,38 @@ public function testConstructWithIMDSv2() */ public function testDefault() { - $this->credential = new EcsRamRoleCredential(); $result = [ 'Expiration' => '2049-10-01 00:00:00', 'AccessKeyId' => 'foo', 'AccessKeySecret' => 'bar', 'SecurityToken' => 'token', + 'Code' => 'Success', ]; + Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(200, [], 'RoleName'); + Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(200, [], $result); + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], 'RoleName'); + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], 'RoleName'); + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], 'RoleName'); + $this->credential = new EcsRamRoleCredential(); self::assertEquals('foo', $this->credential->getAccessKeyId()); self::assertEquals('bar', $this->credential->getAccessKeySecret()); self::assertEquals('token', $this->credential->getSecurityToken()); self::assertEquals(strtotime('2049-10-01 00:00:00'), $this->credential->getExpiration()); + + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], 'RoleName'); + $credentialModel = $this->credential->getCredential(); + $this->assertEquals('foo', $credentialModel->getAccessKeyId()); + $this->assertEquals('bar', $credentialModel->getAccessKeySecret()); + self::assertEquals('token', $credentialModel->getSecurityToken()); + $this->assertEquals('ecs_ram_role', $credentialModel->getType()); } /** @@ -105,17 +128,18 @@ public function testDefault() */ public function testDefault404() { - $this->credential = new EcsRamRoleCredential(); - + Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(404, [], 'RoleName'); + $this->credential = new EcsRamRoleCredential(); + $this->expectException(InvalidArgumentException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { $this->expectExceptionMessageMatches('/The role name was not found in the instance/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { $this->expectExceptionMessageRegExp('/The role name was not found in the instance/'); } - + self::assertEquals('foo', $this->credential->getAccessKeyId()); } @@ -126,16 +150,16 @@ public function testDefault404() */ public function testDefault500() { - $this->credential = new EcsRamRoleCredential(); - + Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(500, [], 'RoleName'); + $this->credential = new EcsRamRoleCredential(); $this->expectException(RuntimeException::class); - + if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/Error retrieving credentials from result: RoleName/'); + $this->expectExceptionMessageMatches('/Error retrieving role name from result: RoleName/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/Error retrieving credentials from result: RoleName/'); + $this->expectExceptionMessageRegExp('/Error retrieving role name from result: RoleName/'); } self::assertEquals('foo', $this->credential->getAccessKeyId()); } @@ -147,16 +171,21 @@ public function testDefault500() */ public function testDefaultEmpty() { + + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], 'RoleNameTest'); + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], []); + $this->credential = new EcsRamRoleCredential(); - Credentials::mockResponse(200, [], ''); $this->expectException(RuntimeException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/Error retrieving credentials from result is empty/'); + $this->expectExceptionMessageMatches('/Error retrieving credentials from IMDS result:\[\]/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/Error retrieving credentials from result is empty/'); + $this->expectExceptionMessageRegExp('/Error retrieving credentials from IMDS result:\[\]/'); } - + self::assertEquals('foo', $this->credential->getAccessKeyId()); } @@ -173,6 +202,7 @@ public function testSts() 'AccessKeyId' => 'foo', 'AccessKeySecret' => 'bar', 'SecurityToken' => 'token', + 'Code' => 'Success', ]; Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(200, [], $result); @@ -199,7 +229,30 @@ public function testStsIncomplete() Credentials::mockResponse(200, [], 'Token'); Credentials::mockResponse(200, [], $result); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Result contains no credentials'); + $this->expectExceptionMessage('Error retrieving credentials from IMDS result:{"Expiration":"2049-10-01 00:00:00","AccessKeyId":"foo"}'); + // Test + self::assertEquals('foo', $credential->getAccessKeyId()); + } + + /** + * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException + * @expectedException \RuntimeException + * @expectedExceptionMessage Result contains no credentials + */ + public function testStsNoCode() + { + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + ]; + $credential = new EcsRamRoleCredential('EcsRamRoleTest2'); + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], $result); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Error retrieving credentials from IMDS result, Code is not Success:{"Expiration":"2049-10-01 00:00:00","AccessKeyId":"foo","AccessKeySecret":"bar","SecurityToken":"token"}'); // Test self::assertEquals('foo', $credential->getAccessKeyId()); } @@ -245,18 +298,18 @@ public function testSts500() $this->expectException(RuntimeException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/Error retrieving credentials from result/'); + $this->expectExceptionMessageMatches('/Error refreshing credentials from IMDS, statusCode: 500, result/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/Error retrieving credentials from result/'); + $this->expectExceptionMessageRegExp('/Error refreshing credentials from IMDS, statusCode: 500, result/'); } - + // Test self::assertEquals('foo', $credential->getAccessKeyId()); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage role_name cannot be empty + * @expectedExceptionMessage roleName cannot be empty */ public function testRoleNameEmpty() { @@ -264,7 +317,7 @@ public function testRoleNameEmpty() $roleName = ''; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('role_name cannot be empty'); + $this->expectExceptionMessage('roleName cannot be empty'); // Test new EcsRamRoleCredential($roleName); } @@ -283,11 +336,57 @@ public function testStsWithoutMock() $this->expectException(RuntimeException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/timed/'); + $this->expectExceptionMessageMatches('/Connection timeout/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/timed/'); + $this->expectExceptionMessageRegExp('/Connection timeout/'); } // Test self::assertEquals('foo', $credential->getAccessKeyId()); } + + public function testGetRoleNameFromMeta() + { + $provider = new EcsRamRoleCredential(); + + Credentials::mockResponse(200, [], 'RoleName'); + + $roleName = $provider->getRoleNameFromMeta(); + self::assertEquals('RoleName', $roleName); + } + + public function testGetRoleNameFromMetaError() + { + $provider = new EcsRamRoleCredential(); + + Credentials::mockResponse(200, [], ''); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Error retrieving credentials from result is empty'); + $provider->getRoleNameFromMeta(); + } + + public function testGetRoleNameFromMeta404() + { + $provider = new EcsRamRoleCredential(); + + Credentials::mockResponse(404, [], 'Error'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The role name was not found in the instance'); + + $provider->getRoleNameFromMeta(); + + } + + public function testRoleNameFromMetaError() + { + $provider = new EcsRamRoleCredential(); + + Credentials::mockResponse(500, [], 'Error'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Error retrieving credentials from result: Error'); + + $provider->getRoleNameFromMeta(); + } } diff --git a/tests/Unit/EcsRamRoleProviderTest.php b/tests/Unit/EcsRamRoleProviderTest.php deleted file mode 100644 index ab47920..0000000 --- a/tests/Unit/EcsRamRoleProviderTest.php +++ /dev/null @@ -1,241 +0,0 @@ - true, - 'metadataTokenDuration' => 3600, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - $sessionConfig = $this->getPrivateField($sessionCredential, 'config'); - - self::assertEquals(true, $sessionConfig['disableIMDSv1']); - self::assertEquals(3600, $sessionConfig['metadataTokenDuration']); - } - - - /** - * @throws Exception - */ - private function invokeProtectedFunc($instance, $method) { - $reflection = new ReflectionClass(EcsRamRoleProvider::class); - $method = $reflection->getMethod($method); - $method->setAccessible(true); - - $result = $method->invoke($instance); - return $result; - } - - /** - * @throws GuzzleException - */ - public function testgetDisableECSIMDSv1() - { - // Setup - $roleName = 'test'; - $config = [ - 'disableIMDSv1' => true, - 'metadataTokenDuration' => 3600, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - $config = [ - 'metadataTokenDuration' => 3600, - ]; - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=true'); - - self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=TRUE'); - - self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=ok'); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=1'); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=false'); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE='); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - } - - private function getPrivateField($instance, $field) { - $reflection = new ReflectionClass(EcsRamRoleProvider::class); - $privateProperty = $reflection->getProperty($field); - $privateProperty->setAccessible(true); - return $privateProperty->getValue($instance); - } - - /** - * @throws GuzzleException - */ - public function testRefreshMetadataTokenDefault() - { - // Setup - $roleName = 'test'; - $config = [ - 'disableIMDSv1' => true, - 'metadataTokenDuration' => 3600, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - Credentials::mockResponse(200, [], 'Token'); - - $token = $this->invokeProtectedFunc($sessionCredential, 'refreshMetadataToken'); - - $histroy = Credentials::getHistroy(); - - $request = end($histroy)['request']; - $headers = $request->getHeaders(); - self::assertEquals('Token', $token); - self::assertEquals('3600', $headers['X-aliyun-ecs-metadata-token-ttl-seconds'][0]); - } - - /** - * @expectedException RuntimeException - * @expectedExceptionMessage Failed to get token from ECS Metadata Service. HttpCode= 404 - * @throws GuzzleException - */ - public function testDefault404() - { - // Setup - $roleName = 'test'; - $config = [ - 'disableIMDSv1' => true, - 'metadataTokenDuration' => 3600, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - Credentials::mockResponse(404, [], 'Error'); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Failed to get token from ECS Metadata Service. HttpCode= 404'); - - $this->invokeProtectedFunc($sessionCredential, 'refreshMetadataToken'); - - } - - public function testEnableV1404() - { - // Setup - $roleName = 'test'; - $config = [ - 'disableIMDSv1' => false, - 'metadataTokenDuration' => 3600, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - Credentials::mockResponse(404, [], 'Error'); - $token = $this->invokeProtectedFunc($sessionCredential, 'refreshMetadataToken'); - - $histroy = Credentials::getHistroy(); - - $request = end($histroy)['request']; - $headers = $request->getHeaders(); - self::assertEquals(null, $token); - self::assertEquals(0, $this->getPrivateField($sessionCredential, 'staleTime')); - - } - - /** - * @throws GuzzleException - */ - public function testNeedToRefresh() - { - // Setup - $roleName = 'test'; - $config = [ - 'disableIMDSv1' => true, - 'metadataTokenDuration' => 5, - ]; - - // Test - $credential = new EcsRamRoleCredential($roleName); - - $sessionCredential = new EcsRamRoleProvider($credential, $config); - - - - self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'needToRefresh')); - - Credentials::mockResponse(200, [], 'Token'); - - $this->invokeProtectedFunc($sessionCredential, 'refreshMetadataToken'); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'needToRefresh')); - - sleep(3); - - self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'needToRefresh')); - - sleep(3); - - self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'needToRefresh')); - } -} diff --git a/tests/Unit/FilterTest.php b/tests/Unit/FilterTest.php deleted file mode 100644 index 69f7f82..0000000 --- a/tests/Unit/FilterTest.php +++ /dev/null @@ -1,55 +0,0 @@ -getMessage()); - } - } - - /** - * @return array - */ - public function accessKey() - { - return [ - [ - ' ', - 'AccessKeySecret', - 'access_key_secret is invalid', - ], - [ - 'AccessKey', - 1, - 'access_key_secret must be a string', - ], - [ - 'AccessKey', - 'AccessKey Secret ', - 'access_key_secret format is invalid', - ], - ]; - } -} diff --git a/tests/Unit/Ini/VirtualCLIConfig.php b/tests/Unit/Ini/VirtualCLIConfig.php new file mode 100644 index 0000000..0710c9c --- /dev/null +++ b/tests/Unit/Ini/VirtualCLIConfig.php @@ -0,0 +1,179 @@ +content = $content; + $this->fileName = $fileName; + } + + /** + * @return string Virtual config.json Filename + */ + public function url() + { + $fileName = 'config.json'; + if ($this->fileName) { + $fileName .= "-$this->fileName"; + } + + return vfsStream::newFile($fileName) + ->withContent($this->content) + ->at(vfsStream::setup('.aliyun')) + ->url(); + } + + /** + * @return string + */ + public static function emptyContent() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function badFormat() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function noMode() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function noName() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function full() + { + $content = <<url(); + } +} diff --git a/tests/Unit/Ini/VirtualOIDCRoleArnCredential.php b/tests/Unit/Ini/VirtualOIDCRoleArnCredential.php new file mode 100644 index 0000000..08dfb82 --- /dev/null +++ b/tests/Unit/Ini/VirtualOIDCRoleArnCredential.php @@ -0,0 +1,85 @@ +url(); + } + + /** + * @return string + */ + public static function noRoleSessionName() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function noTokenFile() + { + $content = <<url(); + } + + /** + * @return string + */ + public static function client($url) + { + $content = <<url(); + } +} diff --git a/tests/Unit/Ini/VirtualRsaKeyPairCredential.php b/tests/Unit/Ini/VirtualRsaKeyPairCredential.php index 6b82e36..f378317 100644 --- a/tests/Unit/Ini/VirtualRsaKeyPairCredential.php +++ b/tests/Unit/Ini/VirtualRsaKeyPairCredential.php @@ -2,7 +2,7 @@ namespace AlibabaCloud\Credentials\Tests\Unit\Ini; -use AlibabaCloud\Credentials\Helper; +use AlibabaCloud\Credentials\Utils\Helper; /** * Class VirtualRsaKeyPairCredential diff --git a/tests/Unit/Providers/CLIProfileCredentialsProviderTest.php b/tests/Unit/Providers/CLIProfileCredentialsProviderTest.php new file mode 100644 index 0000000..2874114 --- /dev/null +++ b/tests/Unit/Providers/CLIProfileCredentialsProviderTest.php @@ -0,0 +1,264 @@ +provider = new CLIProfileCredentialsProvider(); + } + + private function getPrivateField($instance, $field) + { + $reflection = new ReflectionClass(CLIProfileCredentialsProvider::class); + $privateProperty = $reflection->getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + /** + * @throws Exception + */ + private function invokeProtectedFunc($instance, $method, ...$args) + { + $reflection = new ReflectionClass(CLIProfileCredentialsProvider::class); + $method = $reflection->getMethod($method); + $method->setAccessible(true); + + $result = $method->invoke($instance, ...$args); + return $result; + } + + public function testConstruct() + { + // Setup + $provider = new CLIProfileCredentialsProvider(); + + $profileName = $this->getPrivateField($provider, 'profileName'); + self::assertNull($profileName); + + $params = [ + 'profileName' => 'test', + ]; + putenv("ALIBABA_CLOUD_PROFILE=profileName"); + + $provider = new CLIProfileCredentialsProvider($params); + + $profileName = $this->getPrivateField($provider, 'profileName'); + + self::assertEquals('test', $profileName); + self::assertEquals('cli_profile', $provider->getProviderName()); + + putenv("ALIBABA_CLOUD_PROFILE="); + } + + public function testEmpty() + { + $vf = VirtualCLIConfig::emptyContent(); + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Failed to get credential from CLI credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Failed to get credential from CLI credentials file/'); + } + + $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'AK'); + } + + public function testBadFormat() + { + $vf = VirtualCLIConfig::badFormat(); + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Failed to get credential from CLI credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Failed to get credential from CLI credentials file/'); + } + + $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'AK'); + } + + public function testInvalidMode() + { + $vf = VirtualCLIConfig::noMode(); + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Unsupported credential mode from CLI credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Unsupported credential mode from CLI credentials file/'); + } + + $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'AK'); + } + + public function testNoName() + { + $vf = VirtualCLIConfig::noName(); + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Failed to get credential from CLI credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Failed to get credential from CLI credentials file/'); + } + + $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'AK'); + } + + public function testAK() + { + $vf = VirtualCLIConfig::full(); + $provider = new CLIProfileCredentialsProvider(); + $credentialsProvider = $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, ''); + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('access_key_id', $credentials->getAccessKeyId()); + self::assertEquals('access_key_secret', $credentials->getAccessKeySecret()); + self::assertEquals('static_ak', $credentials->getProviderName()); + } + + public function testRamRoleArn() + { + $vf = VirtualCLIConfig::full(); + $provider = new CLIProfileCredentialsProvider(); + + $result = '{ + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + + $credentialsProvider = $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'RamRoleArn'); + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ram_role_arn/static_ak', $credentials->getProviderName()); + + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ram_role_arn/static_ak', $credentials->getProviderName()); + } + + public function testChainableRamRoleArn() + { + $vf = VirtualCLIConfig::full(); + $provider = new CLIProfileCredentialsProvider(); + + $result = '{ + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + + $credentialsProvider = $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'ChainableRamRoleArn'); + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ram_role_arn/static_ak', $credentials->getProviderName()); + + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ram_role_arn/static_ak', $credentials->getProviderName()); + } + + public function testEcsRamRole() + { + $vf = VirtualCLIConfig::full(); + $provider = new CLIProfileCredentialsProvider(); + + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + 'Code' => 'Success', + ]; + + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], $result); + + $credentialsProvider = $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'EcsRamRole'); + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ecs_ram_role', $credentials->getProviderName()); + + $credentials = $credentialsProvider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('ecs_ram_role', $credentials->getProviderName()); + } + + public function testOIDCRoleArn() + { + $vf = VirtualCLIConfig::full(); + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/(?i)Failed to open stream/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/(?i)Failed to open stream/'); + } + + $credentialsProvider = $this->invokeProtectedFunc($provider, 'reloadCredentialsProvider', $vf, 'OIDC'); + $credentialsProvider->getCredentials(); + } + + public function testDisableCLI() + { + putenv("ALIBABA_CLOUD_CLI_PROFILE_DISABLED=true"); + + $provider = new CLIProfileCredentialsProvider(); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/CLI credentials file is disabled/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/CLI credentials file is disabled/'); + } + $provider->getCredentials(); + + putenv("ALIBABA_CLOUD_CLI_PROFILE_DISABLED="); + } +} diff --git a/tests/Unit/Providers/DefaultCredentialsProviderTest.php b/tests/Unit/Providers/DefaultCredentialsProviderTest.php new file mode 100644 index 0000000..914d005 --- /dev/null +++ b/tests/Unit/Providers/DefaultCredentialsProviderTest.php @@ -0,0 +1,270 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No providers in chain'); + DefaultCredentialsProvider::set(); + } + + public function testFlushCustomProviders() + { + DefaultCredentialsProvider::set( + new ProfileCredentialsProvider() + ); + self::assertTrue(DefaultCredentialsProvider::hasCustomChain()); + DefaultCredentialsProvider::flush(); + self::assertFalse(DefaultCredentialsProvider::hasCustomChain()); + } + + public function testGetProviderName() + { + $provider = new DefaultCredentialsProvider(); + self::assertEquals('default', $provider->getProviderName()); + } + + public function testDefaultProviderWithEnv() + { + putenv("ALIBABA_CLOUD_ACCESS_KEY_ID=id"); + putenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET=secret"); + putenv("ALIBABA_CLOUD_SECURITY_TOKEN=token"); + + $provider = new DefaultCredentialsProvider(); + $credentials = $provider->getCredentials(); + self::assertEquals("id", $credentials->getAccessKeyId()); + self::assertEquals("secret", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/env", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_ACCESS_KEY_ID="); + putenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET="); + putenv("ALIBABA_CLOUD_SECURITY_TOKEN="); + } + + public function testDefaultProviderWithOIDC() + { + putenv("ALIBABA_CLOUD_ROLE_ARN=role-arn"); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN=provider-arn"); + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE=$url"); + + $provider = new DefaultCredentialsProvider(); + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/oidc_role_arn", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_ROLE_ARN="); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN="); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE="); + } + + public function testDefaultProviderWithProfile() + { + $vf = VirtualAccessKeyCredential::ok(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=ok"); + $provider = new DefaultCredentialsProvider(); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("default/profile", $credentials->getProviderName()); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + putenv("ALIBABA_CLOUD_PROFILE="); + } + + public function testDefaultProviderWithIMDS() + { + putenv("ALIBABA_CLOUD_ECS_METADATA=roleName"); + + $provider = new DefaultCredentialsProvider(); + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + 'Code' => 'Success', + ]; + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/ecs_ram_role", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_ECS_METADATA="); + } + + public function testDefaultProviderWithURI() + { + putenv("ALIBABA_CLOUD_CREDENTIALS_URI=http://localhost:8080/token"); + + $provider = new DefaultCredentialsProvider(); + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + ]; + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/credential_uri", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_CREDENTIALS_URI="); + } + + public function testDefaultProviderWithReuseLast() + { + // 同时开启OIDC和CredentialsURI + putenv("ALIBABA_CLOUD_ROLE_ARN=test-role-arn"); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN=test-provider-arn"); + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE=$url"); + putenv("ALIBABA_CLOUD_CREDENTIALS_URI=http://localhost:8080/token"); + + $provider = new DefaultCredentialsProvider(); + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + ]; + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/credential_uri", $credentials->getProviderName()); + + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/credential_uri", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_CREDENTIALS_URI="); + putenv("ALIBABA_CLOUD_ROLE_ARN="); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN="); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE="); + } + + public function testDefaultProviderWithUnReuseLast() + { + // 同时开启OIDC和CredentialsURI + putenv("ALIBABA_CLOUD_ROLE_ARN=test-role-arn"); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN=test-provider-arn"); + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE=$url"); + putenv("ALIBABA_CLOUD_CREDENTIALS_URI=http://localhost:8080/token"); + + $provider = new DefaultCredentialsProvider([ + 'reuseLastProviderEnabled' => false, + ]); + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + ]; + Credentials::mockResponse(200, [], $result); + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/credential_uri", $credentials->getProviderName()); + + $credentials = $provider->getCredentials(); + self::assertEquals("foo", $credentials->getAccessKeyId()); + self::assertEquals("bar", $credentials->getAccessKeySecret()); + self::assertEquals("token", $credentials->getSecurityToken()); + self::assertEquals("default/oidc_role_arn", $credentials->getProviderName()); + + putenv("ALIBABA_CLOUD_CREDENTIALS_URI="); + putenv("ALIBABA_CLOUD_ROLE_ARN="); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN="); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE="); + } +} diff --git a/tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php b/tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php new file mode 100644 index 0000000..e82ddf0 --- /dev/null +++ b/tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php @@ -0,0 +1,210 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + /** + * @throws Exception + */ + private function invokeProtectedFunc($instance, $method) { + $reflection = new ReflectionClass(EcsRamRoleCredentialsProvider::class); + $method = $reflection->getMethod($method); + $method->setAccessible(true); + + $result = $method->invoke($instance); + return $result; + } + + public function testConstruct() + { + + // Setup + $params = [ + 'roleName' => 'test', + 'disableIMDSv1' => true, + 'metadataTokenDuration' => 3600, + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + putenv("ALIBABA_CLOUD_ECS_METADATA=roleName"); + putenv("ALIBABA_CLOUD_IMDSV1_DISABLED=false"); + + $provider = new EcsRamRoleCredentialsProvider($params, $config); + + $roleName = $this->getPrivateField($provider, 'roleName'); + $disableIMDSv1 = $this->getPrivateField($provider, 'disableIMDSv1'); + $metadataTokenDuration = $this->getPrivateField($provider, 'metadataTokenDuration'); + + self::assertEquals('test', $roleName); + self::assertEquals(true, $disableIMDSv1); + self::assertEquals(21600, $metadataTokenDuration); + self::assertEquals('test', $provider->getRoleName()); + self::assertEquals(true, $provider->isDisableIMDSv1()); + self::assertEquals('ecs_ram_role', $provider->getProviderName()); + + putenv("ALIBABA_CLOUD_ECS_METADATA="); + putenv("ALIBABA_CLOUD_IMDSV1_DISABLED="); + } + + public function testGetDisableECSIMDSv1() + { + // Setup + $params = [ + 'roleName' => 'test', + 'disableIMDSv1' => true, + ]; + + // Test + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(true, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + $params = [ + 'roleName' => 'test', + ]; + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(false, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=true'); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(true, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=TRUE'); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(true, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=ok'); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(false, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=1'); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(false, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=false'); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(false, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED='); + + $provider = new EcsRamRoleCredentialsProvider($params); + + self::assertEquals(false, $this->invokeProtectedFunc($provider, 'isDisableIMDSv1')); + } + + public function testGetMetadataToken() + { + // Setup + $params = [ + 'roleName' => 'test', + 'disableIMDSv1' => true, + ]; + + // Test + $provider = new EcsRamRoleCredentialsProvider($params); + + Credentials::mockResponse(200, [], 'Token'); + + $token = $this->invokeProtectedFunc($provider, 'getMetadataToken'); + + $histroy = Credentials::getHistroy(); + + $request = end($histroy)['request']; + $headers = $request->getHeaders(); + self::assertEquals('Token', $token); + self::assertEquals('21600', $headers['X-aliyun-ecs-metadata-token-ttl-seconds'][0]); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Failed to get token from ECS Metadata Service. HttpCode= 404 + * @throws GuzzleException + */ + public function testGetMetadataToken404() + { + // Setup + $params = [ + 'roleName' => 'test', + 'disableIMDSv1' => true, + ]; + + // Test + $provider = new EcsRamRoleCredentialsProvider($params); + + Credentials::mockResponse(404, [], 'Error'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Failed to get token from ECS Metadata Service. HttpCode= 404'); + + $this->invokeProtectedFunc($provider, 'getMetadataToken'); + + } + + public function testEnableV1404() + { + // Setup + $params = [ + 'roleName' => 'test', + 'disableIMDSv1' => false, + ]; + + // Test + $provider = new EcsRamRoleCredentialsProvider($params); + + Credentials::mockResponse(404, [], 'Error'); + $token = $this->invokeProtectedFunc($provider, 'getMetadataToken'); + + $histroy = Credentials::getHistroy(); + + $request = end($histroy)['request']; + self::assertEquals(null, $token); + } + +} diff --git a/tests/Unit/Providers/OIDCRoleArnCredentialsProviderTest.php b/tests/Unit/Providers/OIDCRoleArnCredentialsProviderTest.php new file mode 100644 index 0000000..aee1a9f --- /dev/null +++ b/tests/Unit/Providers/OIDCRoleArnCredentialsProviderTest.php @@ -0,0 +1,257 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + public function testConstruct() + { + // Setup + $params = [ + 'roleArn' => 'test', + 'oidcProviderArn' => 'test', + 'roleSessionName' => 'default', + 'oidcTokenFilePath' => '/a/b', + 'durationSeconds' => 3600, + 'policy' => 'policy', + 'stsRegionId' => 'cn-beijing', + 'stsEndpoint' => 'sts.cn-zhangjiakou.aliyuncs.com' + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + putenv("ALIBABA_CLOUD_ROLE_ARN=roleArn"); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN=providerArn"); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE=/b/c"); + putenv("ALIBABA_CLOUD_ROLE_SESSION_NAME=sessionName"); + putenv("ALIBABA_CLOUD_STS_REGION=cn-hangzhou"); + + $provider = new OIDCRoleArnCredentialsProvider($params, $config); + self::assertEquals('oidc_role_arn', $provider->getProviderName()); + self::assertEquals('oidc_role_arn#roleArn#test#oidcProviderArn#test#roleSessionName#default', $provider->key()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $policy = $this->getPrivateField($provider, 'policy'); + $oidcTokenFilePath = $this->getPrivateField($provider, 'oidcTokenFilePath'); + $durationSeconds = $this->getPrivateField($provider, 'durationSeconds'); + self::assertEquals('sts.cn-zhangjiakou.aliyuncs.com', $stsEndpoint); + self::assertEquals('policy', $policy); + self::assertEquals('/a/b', $oidcTokenFilePath); + self::assertEquals(3600, $durationSeconds); + + $provider = new OIDCRoleArnCredentialsProvider([], $config); + self::assertEquals('oidc_role_arn', $provider->getProviderName()); + self::assertEquals('oidc_role_arn#roleArn#roleArn#oidcProviderArn#providerArn#roleSessionName#sessionName', $provider->key()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $policy = $this->getPrivateField($provider, 'policy'); + $oidcTokenFilePath = $this->getPrivateField($provider, 'oidcTokenFilePath'); + $durationSeconds = $this->getPrivateField($provider, 'durationSeconds'); + self::assertEquals('stscn-hangzhou.aliyuncs.com', $stsEndpoint); + self::assertNull($policy); + self::assertEquals('/b/c', $oidcTokenFilePath); + self::assertEquals(3600, $durationSeconds); + + putenv("ALIBABA_CLOUD_ROLE_ARN="); + putenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN="); + putenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE="); + putenv("ALIBABA_CLOUD_ROLE_SESSION_NAME="); + putenv("ALIBABA_CLOUD_STS_REGION="); + } + + public function testConstructErrorRoleArn() + { + // Setup + $params = []; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('roleArn cannot be empty'); + new OIDCRoleArnCredentialsProvider($params, $config); + } + + public function testConstructErroOIDCProviderArn() + { + // Setup + $params = [ + 'roleArn' => 'oidc_role_arn', + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('oidcProviderArn cannot be empty'); + new OIDCRoleArnCredentialsProvider($params, $config); + } + + public function testConstructErroOIDCTokenFilePath() + { + // Setup + $params = [ + 'roleArn' => 'oidc_role_arn', + 'oidcProviderArn' => 'oidc_provider_arn', + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('oidcTokenFilePath cannot be empty'); + new OIDCRoleArnCredentialsProvider($params, $config); + } + + public function testConstructErrorDurationSeconds() + { + // Setup + $params = [ + 'roleArn' => 'test', + 'oidcProviderArn' => 'oidc_provider_arn', + 'oidcTokenFilePath' => 'oidc_token_file_path', + 'durationSeconds' => 800, + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Role session expiration should be in the range of 900s - max session duration'); + new OIDCRoleArnCredentialsProvider($params, $config); + } + + public function testSts() + { + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + $params = [ + 'roleArn' => 'testSts', + 'oidcProviderArn' => 'oidc_provider_arn', + 'oidcTokenFilePath' => $url, + 'policy' => [], + 'externalId' => 'externalId', + ]; + $provider = new OIDCRoleArnCredentialsProvider($params); + + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } + }'; + Credentials::mockResponse(200, [], $result); + $credential = $provider->getCredentials(); + + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + self::assertEquals('oidc_role_arn', $credential->getProviderName()); + + $credential = $provider->getCredentials(); + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + self::assertEquals('oidc_role_arn', $credential->getProviderName()); + } + + public function testStsIncomplete() + { + // Setup + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + $params = [ + 'roleArn' => 'test', + 'oidcProviderArn' => 'oidc_provider_arn', + 'oidcTokenFilePath' => $url, + ]; + $provider = new OIDCRoleArnCredentialsProvider($params); + + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeyId": "STS.**************", + "Expiration": "2020-02-25T03:56:19Z", + "SecurityToken": "**************" + } + }'; + Credentials::mockResponse(200, [], $result); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error retrieving credentials from OIDC/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error retrieving credentials from OIDC/'); + } + // Test + $provider->getCredentials(); + } + + public function testSts500() + { + // Setup + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + $params = [ + 'roleArn' => 'test', + 'oidcProviderArn' => 'oidc_provider_arn', + 'oidcTokenFilePath' => $url, + ]; + $provider = new OIDCRoleArnCredentialsProvider($params); + Credentials::mockResponse(500, [], 'Error'); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error refreshing credentials from OIDC, statusCode: 500, result: Error/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error refreshing credentials from OIDC, statusCode: 500, result: Error/'); + } + // Test + $provider->getCredentials(); + } +} diff --git a/tests/Unit/Providers/ProfileCredentialsProviderTest.php b/tests/Unit/Providers/ProfileCredentialsProviderTest.php new file mode 100644 index 0000000..0d634e4 --- /dev/null +++ b/tests/Unit/Providers/ProfileCredentialsProviderTest.php @@ -0,0 +1,289 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + public function testConstruct() + { + // Setup + $provider = new ProfileCredentialsProvider(); + + $profileName = $this->getPrivateField($provider, 'profileName'); + $profileFile = $this->getPrivateField($provider, 'profileFile'); + self::assertEquals('default', $profileName); + self::assertTrue(strpos($profileFile, '.alibabacloud' . DIRECTORY_SEPARATOR . 'credentials') !== false); + + $params = [ + 'profileName' => 'test', + ]; + putenv("ALIBABA_CLOUD_PROFILE=profileName"); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=/a/b"); + + $provider = new ProfileCredentialsProvider($params); + + $profileName = $this->getPrivateField($provider, 'profileName'); + $profileFile = $this->getPrivateField($provider, 'profileFile'); + + self::assertEquals('test', $profileName); + self::assertEquals('/a/b', $profileFile); + self::assertEquals('profile', $provider->getProviderName()); + + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testInvalidType() + { + $vf = VirtualAccessKeyCredential::invalidType(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Unsupported credential type from credentials file: invalidType/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Unsupported credential type from credentials file: invalidType/'); + } + $provider = new ProfileCredentialsProvider(); + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testAK() + { + $vf = VirtualAccessKeyCredential::ok(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=ok"); + $provider = new ProfileCredentialsProvider(); + $credentials = $provider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('profile/static_ak', $credentials->getProviderName()); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testAKNoKeyError() + { + $vf = VirtualAccessKeyCredential::noKey(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/accessKeyId must be a string/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/accessKeyId must be a string/'); + } + $provider = new ProfileCredentialsProvider(); + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testAKNoTypeError() + { + $vf = VirtualAccessKeyCredential::noType(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Unsupported credential type from credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Unsupported credential type from credentials file/'); + } + $provider = new ProfileCredentialsProvider(); + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testRamRoleArn() + { + $vf = VirtualRamRoleArnCredential::client(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + + $result = '{ + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } +}'; + Credentials::mockResponse(200, [], $result); + + $credentials = $provider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('profile/ram_role_arn/static_ak', $credentials->getProviderName()); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testRamRoleArnError() + { + $vf = VirtualRamRoleArnCredential::noRoleArn(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/roleArn cannot be empty/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/roleArn cannot be empty/'); + } + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testEcsRamRole() + { + $vf = VirtualEcsRamRoleCredential::client(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + + $result = [ + 'Expiration' => '2049-10-01 00:00:00', + 'AccessKeyId' => 'foo', + 'AccessKeySecret' => 'bar', + 'SecurityToken' => 'token', + 'Code' => 'Success', + ]; + + Credentials::mockResponse(200, [], 'Token'); + Credentials::mockResponse(200, [], $result); + + $credentials = $provider->getCredentials(); + self::assertEquals('foo', $credentials->getAccessKeyId()); + self::assertEquals('bar', $credentials->getAccessKeySecret()); + self::assertEquals('profile/ecs_ram_role', $credentials->getProviderName()); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testOIDCRoleArn() + { + $vf = new VirtualFile("token"); + $url = $vf->url("token-file"); + $vf = VirtualOIDCRoleArnCredential::client($url); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/(?i)failed to open stream/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/(?i)failed to open stream/'); + } + $provider = new ProfileCredentialsProvider(); + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testOIDCRoleArnError() + { + $vf = VirtualOIDCRoleArnCredential::noRoleArn(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/roleArn cannot be empty/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/roleArn cannot be empty/'); + } + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testRsaKeyPairNoPrivateKeyFile() + { + $vf = VirtualRsaKeyPairCredential::noPrivateKeyFile(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/privateKeyFile must be a string/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/privateKeyFile must be a string/'); + } + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testRsaKeyPairNoPublicKeyId() + { + $vf = VirtualRsaKeyPairCredential::noPublicKeyId(); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + putenv("ALIBABA_CLOUD_PROFILE=phpunit"); + $provider = new ProfileCredentialsProvider(); + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/publicKeyId must be a string/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/publicKeyId must be a string/'); + } + $provider->getCredentials(); + putenv("ALIBABA_CLOUD_PROFILE="); + putenv("ALIBABA_CLOUD_CREDENTIALS_FILE="); + } + + public function testSetIniError() + { + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Unable to open credentials file/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Unable to open credentials file/'); + } + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE=/c/d'); + $provider = new ProfileCredentialsProvider(); + $provider->getCredentials(); + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE='); + } +} diff --git a/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php b/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php new file mode 100644 index 0000000..6b97ab4 --- /dev/null +++ b/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php @@ -0,0 +1,252 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + public function testConstruct() + { + // Setup + $params = [ + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'roleArn' => 'test', + 'roleSessionName' => 'default', + 'durationSeconds' => 3600, + 'policy' => 'policy', + 'externalId' => 'externalId', + 'stsRegionId' => 'cn-beijing', + 'stsEndpoint' => 'sts.cn-zhangjiakou.aliyuncs.com' + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + putenv("ALIBABA_CLOUD_ROLE_ARN=roleArn"); + putenv("ALIBABA_CLOUD_ROLE_SESSION_NAME=sessionName"); + putenv("ALIBABA_CLOUD_STS_REGION=cn-hangzhou"); + + $provider = new RamRoleArnCredentialsProvider($params, $config); + + self::assertEquals('foo', $provider->getOriginalAccessKeyId()); + self::assertEquals('bar', $provider->getOriginalAccessKeySecret()); + self::assertEquals('test', $provider->getRoleArn()); + self::assertEquals('default', $provider->getRoleSessionName()); + self::assertEquals('policy', $provider->getPolicy()); + self::assertEquals('ram_role_arn/static_ak', $provider->getProviderName()); + self::assertEquals('ram_role_arn#credential#foo#roleArn#test#roleSessionName#default', $provider->key()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $externalId = $this->getPrivateField($provider, 'externalId'); + self::assertEquals('sts.cn-zhangjiakou.aliyuncs.com', $stsEndpoint); + self::assertEquals('externalId', $externalId); + + $params = [ + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + 'securityToken' => 'token', + ]; + $provider = new RamRoleArnCredentialsProvider($params, $config); + self::assertEquals('foo', $provider->getOriginalAccessKeyId()); + self::assertEquals('bar', $provider->getOriginalAccessKeySecret()); + self::assertEquals('roleArn', $provider->getRoleArn()); + self::assertEquals('sessionName', $provider->getRoleSessionName()); + self::assertNull($provider->getPolicy()); + self::assertEquals('ram_role_arn/static_sts', $provider->getProviderName()); + self::assertEquals('ram_role_arn#credential#foo#roleArn#roleArn#roleSessionName#sessionName', $provider->key()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $externalId = $this->getPrivateField($provider, 'externalId'); + self::assertEquals('sts.cn-hangzhou.aliyuncs.com', $stsEndpoint); + self::assertNull($externalId); + + putenv("ALIBABA_CLOUD_ROLE_ARN="); + putenv("ALIBABA_CLOUD_ROLE_SESSION_NAME="); + putenv("ALIBABA_CLOUD_STS_REGION="); + } + + public function testConstructErrorCredentials() + { + // Setup + $params = []; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Missing required credentials option for ram_role_arn'); + new RamRoleArnCredentialsProvider($params, $config); + } + + public function testConstructErrorDurationSeconds() + { + // Setup + $params = [ + 'credentialsProvider' => new StaticAKCredentialsProvider([ + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + ]), + 'roleArn' => 'test', + 'durationSeconds' => 800, + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Role session expiration should be in the range of 900s - max session duration'); + new RamRoleArnCredentialsProvider($params, $config); + } + + public function testSts() + { + $params = [ + 'accessKeyId' => 'test', + 'accessKeySecret' => 'test', + 'roleArn' => 'testSts', + 'policy' => [], + 'externalId' => 'externalId', + ]; + $provider = new RamRoleArnCredentialsProvider($params); + + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + } + }'; + Credentials::mockResponse(200, [], $result); + $credential = $provider->getCredentials(); + + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + self::assertEquals('ram_role_arn/static_ak', $credential->getProviderName()); + + $credential = $provider->getCredentials(); + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + self::assertEquals('ram_role_arn/static_ak', $credential->getProviderName()); + } + + public function testStsIncomplete() + { + // Setup + $params = [ + 'accessKeyId' => 'test', + 'accessKeySecret' => 'test', + 'roleArn' => 'test', + ]; + $provider = new RamRoleArnCredentialsProvider($params); + + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeyId": "STS.**************", + "Expiration": "2020-02-25T03:56:19Z", + "SecurityToken": "**************" + } + }'; + Credentials::mockResponse(200, [], $result); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error retrieving credentials from RamRoleArn/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error retrieving credentials from RamRoleArn/'); + } + // Test + $provider->getCredentials(); + } + + public function testSts500() + { + // Setup + $params = [ + 'accessKeyId' => 'test', + 'accessKeySecret' => 'test', + 'roleArn' => 'test', + ]; + $provider = new RamRoleArnCredentialsProvider($params); + Credentials::mockResponse(500, [], 'Error'); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error refreshing credentials from RamRoleArn, statusCode: 500, result: Error/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error refreshing credentials from RamRoleArn, statusCode: 500, result: Error/'); + } + // Test + $provider->getCredentials(); + } + + public function testAccessKeyIdEmpty() + { + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('accessKeyId cannot be empty'); + // Test + $params = [ + 'accessKeyId' => '', + 'accessKeySecret' => 'test', + 'roleArn' => 'test', + ]; + new RamRoleArnCredentialsProvider($params); + } + + public function testAccessKeyFormat() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Missing required credentials option for ram_role_arn'); + // Test + $params = [ + 'accessKeyId' => 'test', + 'accessKeySecret' => null, + 'roleArn' => 'test', + ]; + new RamRoleArnCredentialsProvider($params); + } +} diff --git a/tests/Unit/Providers/RsaKeyPairCredentialsProviderTest.php b/tests/Unit/Providers/RsaKeyPairCredentialsProviderTest.php new file mode 100644 index 0000000..1b63cac --- /dev/null +++ b/tests/Unit/Providers/RsaKeyPairCredentialsProviderTest.php @@ -0,0 +1,213 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + public function testConstruct() + { + // Setup + $url = VirtualRsaKeyPairCredential::badPrivateKey(); + $params = [ + 'publicKeyId' => 'test', + 'privateKeyFile' => $url, + 'stsEndpoint' => 'sts.aliyuncs.com', + 'durationSeconds' => 6000, + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + + $provider = new RsaKeyPairCredentialsProvider($params, $config); + + self::assertEquals('rsa_key_pair', $provider->getProviderName()); + self::assertEquals('rsa_key_pair#publicKeyId#test', $provider->key()); + self::assertEquals(file_get_contents($url), $provider->getPrivateKey()); + self::assertEquals('test', $provider->getPublicKeyId()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $durationSeconds = $this->getPrivateField($provider, 'durationSeconds'); + self::assertEquals('sts.aliyuncs.com', $stsEndpoint); + self::assertEquals(6000, $durationSeconds); + + $params = [ + 'publicKeyId' => 'test', + 'privateKeyFile' => $url, + ]; + $provider = new RsaKeyPairCredentialsProvider($params, $config); + + self::assertEquals('rsa_key_pair', $provider->getProviderName()); + self::assertEquals('rsa_key_pair#publicKeyId#test', $provider->key()); + self::assertEquals(file_get_contents($url), $provider->getPrivateKey()); + self::assertEquals('test', $provider->getPublicKeyId()); + $stsEndpoint = $this->getPrivateField($provider, 'stsEndpoint'); + $durationSeconds = $this->getPrivateField($provider, 'durationSeconds'); + self::assertEquals('sts.ap-northeast-1.aliyuncs.com', $stsEndpoint); + self::assertEquals(3600, $durationSeconds); + + } + + public function testConstructErrorPublicKeyId() + { + // Setup + $params = []; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('publicKeyId must be a string'); + new RsaKeyPairCredentialsProvider($params, $config); + } + + public function testConstructErrorPrivateKeyFile() + { + // Setup + $params = [ + 'publicKeyId' => 'test', + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('privateKeyFile must be a string'); + new RsaKeyPairCredentialsProvider($params, $config); + } + + public function testConstructErrorDurationSeconds() + { + // Setup + $url = VirtualRsaKeyPairCredential::badPrivateKey(); + $params = [ + 'publicKeyId' => 'test', + 'privateKeyFile' => $url, + 'durationSeconds' => 800, + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Role session expiration should be in the range of 900s - max session duration'); + new RsaKeyPairCredentialsProvider($params, $config); + } + + public function testSts() + { + $url = VirtualRsaKeyPairCredential::privateKeyFileUrl(); + $params = [ + 'publicKeyId' => 'testSts', + 'privateKeyFile' => $url, + ]; + $provider = new RsaKeyPairCredentialsProvider($params); + + $result = '{ + "RequestId": "F702286E-F231-4F40-BB86-XXXXXX", + "SessionAccessKey": { + "SessionAccessKeyId": "foo", + "Expiration": "2049-10-01T07:02:36.225Z", + "SessionAccessKeySecret": "bar" + } +}'; + Credentials::mockResponse(200, [], $result); + $credential = $provider->getCredentials(); + + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('', $credential->getSecurityToken()); + self::assertEquals('rsa_key_pair', $credential->getProviderName()); + + $credential = $provider->getCredentials(); + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('', $credential->getSecurityToken()); + self::assertEquals('rsa_key_pair', $credential->getProviderName()); + } + + public function testStsIncomplete() + { + // Setup + $url = VirtualRsaKeyPairCredential::privateKeyFileUrl(); + $params = [ + 'publicKeyId' => 'test', + 'privateKeyFile' => $url, + ]; + $provider = new RsaKeyPairCredentialsProvider($params); + + $result = '{ + "RequestId": "F702286E-F231-4F40-BB86-XXXXXX", + "SessionAccessKey": { + "SessionAccessKeyId": "TMPSK.**************", + "Expiration": "2023-02-19T07:02:36.225Z" + } +}'; + Credentials::mockResponse(200, [], $result); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error retrieving credentials from RsaKeyPair/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error retrieving credentials from RsaKeyPair/'); + } + // Test + $provider->getCredentials(); + } + + public function testSts500() + { + // Setup + $url = VirtualRsaKeyPairCredential::privateKeyFileUrl(); + $params = [ + 'publicKeyId' => 'test', + 'privateKeyFile' => $url, + ]; + $provider = new RsaKeyPairCredentialsProvider($params); + Credentials::mockResponse(500, [], 'Error'); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error refreshing credentials from RsaKeyPair, statusCode: 500, result: Error/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error refreshing credentials from RsaKeyPair, statusCode: 500, result: Error/'); + } + // Test + $provider->getCredentials(); + } + +} diff --git a/tests/Unit/Providers/StaticCredentialsProviderTest.php b/tests/Unit/Providers/StaticCredentialsProviderTest.php new file mode 100644 index 0000000..5a8a1ac --- /dev/null +++ b/tests/Unit/Providers/StaticCredentialsProviderTest.php @@ -0,0 +1,75 @@ + 'test', + 'accessKeySecret' => 'test', + 'securityToken' => 'test', + ]; + putenv("ALIBABA_CLOUD_ACCESS_KEY_ID=id"); + putenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET=secret"); + putenv("ALIBABA_CLOUD_SECURITY_TOKEN=token"); + + $provider = new StaticSTSCredentialsProvider($params); + $credential = $provider->getCredentials(); + + self::assertEquals('test', $credential->getAccessKeyId()); + self::assertEquals('test', $credential->getAccessKeySecret()); + self::assertEquals('test', $credential->getSecurityToken()); + self::assertEquals('static_sts', $credential->getProviderName()); + + $provider = new StaticAKCredentialsProvider($params); + $credential = $provider->getCredentials(); + + self::assertEquals('test', $credential->getAccessKeyId()); + self::assertEquals('test', $credential->getAccessKeySecret()); + self::assertEquals('', $credential->getSecurityToken()); + self::assertEquals('static_ak', $credential->getProviderName()); + + $provider = new StaticSTSCredentialsProvider([]); + $credential = $provider->getCredentials(); + + self::assertEquals('id', $credential->getAccessKeyId()); + self::assertEquals('secret', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + self::assertEquals('static_sts', $credential->getProviderName()); + + $provider = new StaticAKCredentialsProvider([]); + $credential = $provider->getCredentials(); + + self::assertEquals('id', $credential->getAccessKeyId()); + self::assertEquals('secret', $credential->getAccessKeySecret()); + self::assertEquals('', $credential->getSecurityToken()); + self::assertEquals('static_ak', $credential->getProviderName()); + + putenv("ALIBABA_CLOUD_ACCESS_KEY_ID="); + putenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET="); + putenv("ALIBABA_CLOUD_SECURITY_TOKEN="); + } +} diff --git a/tests/Unit/Providers/URLCredentialsProviderTest.php b/tests/Unit/Providers/URLCredentialsProviderTest.php new file mode 100644 index 0000000..5bfcfa1 --- /dev/null +++ b/tests/Unit/Providers/URLCredentialsProviderTest.php @@ -0,0 +1,148 @@ +getProperty($field); + $privateProperty->setAccessible(true); + return $privateProperty->getValue($instance); + } + + public function testConstruct() + { + // Setup + $params = [ + 'credentialsURI' => 'http://credentials.aliyun.com', + ]; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + putenv("ALIBABA_CLOUD_CREDENTIALS_URI=http://test.aliyun.com"); + + $provider = new URLCredentialsProvider($params, $config); + + self::assertEquals('credential_uri', $provider->getProviderName()); + self::assertEquals('credential_uri#http://credentials.aliyun.com', $provider->key()); + $credentialsURI = $this->getPrivateField($provider, 'credentialsURI'); + self::assertEquals('http://credentials.aliyun.com', $credentialsURI); + + $provider = new URLCredentialsProvider([], $config); + self::assertEquals('credential_uri', $provider->getProviderName()); + self::assertEquals('credential_uri#http://test.aliyun.com', $provider->key()); + $credentialsURI = $this->getPrivateField($provider, 'credentialsURI'); + self::assertEquals('http://test.aliyun.com', $credentialsURI); + + putenv("ALIBABA_CLOUD_CREDENTIALS_URI="); + } + + public function testConstructError() + { + // Setup + $params = []; + $config = [ + 'connectTimeout' => 10, + 'readTimeout' => 10, + ]; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('credentialsURI must be a string'); + new URLCredentialsProvider($params, $config); + } + + public function testSts() + { + $params = [ + 'credentialsURI' => 'http://credentials.aliyun.com', + ]; + $provider = new URLCredentialsProvider($params); + + $result = '{ + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "Expiration": "2049-10-25T03:56:19Z", + "SecurityToken": "token" + }'; + Credentials::mockResponse(200, [], $result); + $credential = $provider->getCredentials(); + + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + + $credential = $provider->getCredentials(); + self::assertEquals('foo', $credential->getAccessKeyId()); + self::assertEquals('bar', $credential->getAccessKeySecret()); + self::assertEquals('token', $credential->getSecurityToken()); + } + + public function testStsIncomplete() + { + // Setup + $params = [ + 'credentialsURI' => 'test', + ]; + $provider = new URLCredentialsProvider($params); + + $result = '{ + "AccessKeySecret": "bar", + "AccessKeyId": "foo", + "SecurityToken": "token" + }'; + Credentials::mockResponse(200, [], $result); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error retrieving credentials from credentialsURI/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error retrieving credentials from credentialsURI/'); + } + // Test + $provider->getCredentials(); + } + + public function testSts500() + { + // Setup + $params = [ + 'credentialsURI' => 'test', + ]; + $provider = new URLCredentialsProvider($params); + Credentials::mockResponse(500, [], 'Error'); + + $this->expectException(RuntimeException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/Error refreshing credentials from credentialsURI, statusCode: 500, result: Error/'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/Error refreshing credentials from credentialsURI, statusCode: 500, result: Error/'); + } + // Test + $provider->getCredentials(); + } +} diff --git a/tests/Unit/RamRoleArnCredentialTest.php b/tests/Unit/RamRoleArnCredentialTest.php index 789d4da..39476b6 100644 --- a/tests/Unit/RamRoleArnCredentialTest.php +++ b/tests/Unit/RamRoleArnCredentialTest.php @@ -39,6 +39,8 @@ public function testConstruct() ]); // Assert + $this->assertEquals($accessKeyId, $credential->getOriginalAccessKeyId()); + $this->assertEquals($accessKeySecret, $credential->getOriginalAccessKeySecret()); $this->assertEquals($arn, $credential->getRoleArn()); $this->assertEquals($sessionName, $credential->getRoleSessionName()); $this->assertEquals($policy, $credential->getPolicy()); @@ -80,7 +82,7 @@ public function testSts() $credential = new RamRoleArnCredential([ 'access_key_id' => 'access_key_id', 'access_key_secret' => 'access_key_secret', - 'role_arn' => 'role_arn', + 'role_arn' => 'role_arn1', 'role_session_name' => 'role_session_name', 'policy' => [], ]); @@ -89,6 +91,13 @@ public function testSts() self::assertEquals('********************', $credential->getAccessKeySecret()); self::assertEquals('**************', $credential->getSecurityToken()); self::assertEquals(strtotime('2020-02-25T03:56:19Z'), $credential->getExpiration()); + + Credentials::mockResponse(200, [], $result); + $credentialModel = $credential->getCredential(); + $this->assertEquals('STS.**************', $credentialModel->getAccessKeyId()); + $this->assertEquals('********************', $credentialModel->getAccessKeySecret()); + self::assertEquals('**************', $credentialModel->getSecurityToken()); + $this->assertEquals('ram_role_arn', $credentialModel->getType()); } /** @@ -122,7 +131,7 @@ public function testStsIncomplete() 'policy' => '', ]); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Result contains no credentials'); + $this->expectExceptionMessage('Error retrieving credentials from RamRoleArn result:{"RequestId":"88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44","AssumedRoleUser":{"AssumedRoleId":"********************","Arn":"********************"},"Credentials":{"AccessKeyId":"STS.**************","Expiration":"2020-02-25T03:56:19Z","SecurityToken":"**************"}}'); // Test self::assertEquals('TMPSK.**************', $credential->getAccessKeyId()); } @@ -135,7 +144,7 @@ public function testAccessKeyIdEmpty() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_id cannot be empty'); + $this->expectExceptionMessage('accessKeyId cannot be empty'); // Test new RamRoleArnCredential([ 'access_key_id' => '', diff --git a/tests/Unit/Request/RequestTest.php b/tests/Unit/Request/RequestTest.php new file mode 100644 index 0000000..0e61c1c --- /dev/null +++ b/tests/Unit/Request/RequestTest.php @@ -0,0 +1,101 @@ + 'access_key', + 'accessKeyId' => 'foo', + 'accessKeySecret' => 'bar', + ]; + + // Assert + static::assertEquals("get&%2F&accessKeyId%3Dfoo%26accessKeySecret%3Dbar%26type%3Daccess_key", Request::signString($method, $request)); + } + + public function testShaHmac1Signature() + { + // Setup + $string = 'this is a ShaHmac1 test.'; + $accessKeySecret = 'accessKeySecret'; + $expected = 'PEr0Vp78B4Fslzf54dzdXD4Qt3E='; + + // Assert + static::assertEquals($expected, Request::shaHmac1sign($string, $accessKeySecret)); + } + + public function testShaHmac256Signature() + { + // Setup + $string = 'this is a ShaHmac256 test.'; + $accessKeySecret = 'accessKeySecret'; + $expected = 'v1Kg2HYGWRaLsRu2iXkAZu3R7vDh0txyYHs48HVxkeA='; + + // Assert + static::assertEquals($expected, Request::shaHmac256sign($string, $accessKeySecret)); + } + + public function testShaHmac256WithRsaSignature() + { + // Setup + $string = 'string'; + $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); + $expected = + 'gjWgwRf/BjOYbjrPleU9qzNrZXNO+Z9aiwxmbBj1TPF2/PEOjBy5/YSk+GfL2GGg5pkupzrKiG+4FQ4r+EjeQcdByRDv1x1eBrQHwAbieKmjLc1++vJWQQpSKJykMl5dRzADUwsXYzvCCvVCIXjYZJNsrdt/0G+gaRVX7oelAX+d1MiTjRam7Ugzxcr1nELz2dc3DOyhXqCw8loNtsFVNcrDC5B/urx4eYdAFWRYVbORdTTgPdOF/gNJOWPqQgvFQsICJpScwIXP2OntCjYj8EBGNafBK3bCe4jxHwtxBA72PmuJ/ZHxUqSstwbcVk5S40PlRIhqtrfn6ajxYk41SQ=='; + + // Assert + static::assertEquals($expected, Request::shaHmac256WithRsasign($string, \file_get_contents($privateKeyFile))); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionCode 0 + * @expectedExceptionMessage openssl_sign(): supplied key param cannot be coerced into a private key + */ + public function testShaHmac256WithRsaSignatureBadPrivateKey() + { + // Setup + $string = 'string'; + $privateKeyFile = VirtualRsaKeyPairCredential::badPrivateKey(); + + $this->expectException(InvalidArgumentException::class); + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches('/supplied key param cannot be coerced into a private key/i'); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp('/supplied key param cannot be coerced into a private key/i'); + } + + Request::shaHmac256WithRsasign($string, \file_get_contents($privateKeyFile)); + } +} \ No newline at end of file diff --git a/tests/Unit/RsaKeyPairCredentialTest.php b/tests/Unit/RsaKeyPairCredentialTest.php index 55cb084..ba9e483 100644 --- a/tests/Unit/RsaKeyPairCredentialTest.php +++ b/tests/Unit/RsaKeyPairCredentialTest.php @@ -3,7 +3,7 @@ namespace AlibabaCloud\Credentials\Tests\Unit; use AlibabaCloud\Credentials\Credentials; -use AlibabaCloud\Credentials\Helper; +use AlibabaCloud\Credentials\Utils\Helper; use AlibabaCloud\Credentials\RsaKeyPairCredential; use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use AlibabaCloud\Credentials\Tests\Unit\Ini\VirtualRsaKeyPairCredential; @@ -64,6 +64,7 @@ public static function testOpenBasedirException() $e->getMessage() ); } + ini_set('open_basedir', null); } public function testConstruct() @@ -85,6 +86,7 @@ public function testConstruct() $this->assertEquals([], $credential->getConfig()); $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals($publicKeyId, $credential->getOriginalAccessKeyId()); + $this->assertNotEmpty($credential->getOriginalAccessKeySecret()); } /** @@ -115,6 +117,13 @@ public function testSts() self::assertEquals('**************', $credential->getAccessKeySecret()); self::assertEquals('', $credential->getSecurityToken()); self::assertEquals(strtotime('2023-02-19T07:02:36.225Z'), $credential->getExpiration()); + + Credentials::mockResponse(200, [], $result); + $credentialModel = $this->credential->getCredential(); + $this->assertEquals('TMPSK.**************', $credentialModel->getAccessKeyId()); + $this->assertEquals('**************', $credentialModel->getAccessKeySecret()); + self::assertEquals('', $credentialModel->getSecurityToken()); + $this->assertEquals('rsa_key_pair', $credentialModel->getType()); } /** @@ -139,7 +148,7 @@ public function testStsIncomplete() Credentials::mockResponse(200, [], $result); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Result contains no credentials'); + $this->expectExceptionMessage('Error retrieving credentials from RsaKeyPair result:{"RequestId":"F702286E-F231-4F40-BB86-XXXXXX","SessionAccessKey":{"SessionAccessKeyId":"TMPSK.**************","Expiration":"2023-02-19T07:02:36.225Z"}}'); $credential = new RsaKeyPairCredential($publicKeyId, $privateKeyFile); @@ -158,7 +167,7 @@ public function testPublicKeyIdEmpty() $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('public_key_id cannot be empty'); + $this->expectExceptionMessage('publicKeyId cannot be empty'); // Test new RsaKeyPairCredential($publicKeyId, $privateKeyFile); } @@ -174,7 +183,7 @@ public function testPublicKeyIdFormat() $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('public_key_id must be a string'); + $this->expectExceptionMessage('publicKeyId must be a string'); // Test new RsaKeyPairCredential($publicKeyId, $privateKeyFile); } @@ -190,7 +199,7 @@ public function testPrivateKeyFileEmpty() $privateKeyFile = ''; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('private_key_file cannot be empty'); + $this->expectExceptionMessage('privateKeyFile cannot be empty'); // Test new RsaKeyPairCredential($publicKeyId, $privateKeyFile); } @@ -206,7 +215,7 @@ public function testPrivateKeyFileFormat() $privateKeyFile = null; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('private_key_file must be a string'); + $this->expectExceptionMessage('privateKeyFile must be a string'); // Test new RsaKeyPairCredential($publicKeyId, $privateKeyFile); } diff --git a/tests/Unit/StsCredentialTest.php b/tests/Unit/StsCredentialTest.php index 49e9a8d..67c92c0 100644 --- a/tests/Unit/StsCredentialTest.php +++ b/tests/Unit/StsCredentialTest.php @@ -2,8 +2,8 @@ namespace AlibabaCloud\Credentials\Tests\Unit; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use AlibabaCloud\Credentials\StsCredential; +use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use PHPUnit\Framework\TestCase; use InvalidArgumentException; @@ -30,6 +30,12 @@ public function testConstruct() "$accessKeyId#$accessKeySecret#$securityToken", (string)$credential ); + + $credentialModel = $credential->getCredential(); + $this->assertEquals($accessKeyId, $credentialModel->getAccessKeyId()); + $this->assertEquals($accessKeySecret, $credentialModel->getAccessKeySecret()); + self::assertEquals($securityToken, $credentialModel->getSecurityToken()); + $this->assertEquals('sts', $credentialModel->getType()); } /** @@ -44,7 +50,7 @@ public function testAccessKeyIdEmpty() $securityToken = 'securityToken'; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_id cannot be empty'); + $this->expectExceptionMessage('accessKeyId cannot be empty'); new StsCredential($accessKeyId, $accessKeySecret, $securityToken); } @@ -61,7 +67,7 @@ public function testAccessKeyIdFormat() $securityToken = 'securityToken'; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_id must be a string'); + $this->expectExceptionMessage('accessKeyId must be a string'); new StsCredential($accessKeyId, $accessKeySecret, $securityToken); } @@ -78,7 +84,7 @@ public function testAccessKeySecretEmpty() $securityToken = 'securityToken'; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('access_key_secret cannot be empty'); + $this->expectExceptionMessage('accessKeySecret cannot be empty'); new StsCredential($accessKeyId, $accessKeySecret, $securityToken); } @@ -96,7 +102,7 @@ public function testAccessKeySecretFormat() $this->expectException(InvalidArgumentException::class); $this->expectExceptionCode(0); - $this->expectExceptionMessage('access_key_secret must be a string'); + $this->expectExceptionMessage('accessKeySecret must be a string'); new StsCredential($accessKeyId, $accessKeySecret, $securityToken); } diff --git a/tests/Unit/Utils/FilterTest.php b/tests/Unit/Utils/FilterTest.php new file mode 100644 index 0000000..4ef7149 --- /dev/null +++ b/tests/Unit/Utils/FilterTest.php @@ -0,0 +1,332 @@ +getMessage()); + } + } + + /** + * @return array + */ + public function accessKey() + { + return [ + [ + '', + 'AccessKeySecret', + 'accessKeyId cannot be empty', + ], + [ + 'AccessKey', + '', + 'accessKeySecret cannot be empty', + ], + [ + 1, + 'AccessKeySecret', + 'accessKeyId must be a string', + ], + [ + 'AccessKey', + 1, + 'accessKeySecret must be a string', + ] + ]; + } + + /** + * @dataProvider timeout + * + * @param string $connectTimeout + * @param string $readTimeout + * @param string $exceptionMessage + */ + public function testTimeout($connectTimeout, $readTimeout, $exceptionMessage) + { + try { + Filter::timeout($connectTimeout, $readTimeout); + } catch (Exception $exception) { + self::assertEquals($exceptionMessage, $exception->getMessage()); + } + } + + /** + * @return array + */ + public function timeout() + { + return [ + [ + '', + 1, + 'connectTimeout must be a int', + ], + [ + 1, + '', + 'readTimeout must be a int', + ] + ]; + } + + public function testCredentialName() + { + try { + Filter::credentialName(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('Name must be a string', $exception->getMessage()); + } + try { + Filter::credentialName(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('Name must be a string', $exception->getMessage()); + } + try { + Filter::credentialName(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('Name cannot be empty', $exception->getMessage()); + } + Filter::credentialName('1'); + } + + public function testBearerToken() + { + try { + Filter::bearerToken(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('bearerToken must be a string', $exception->getMessage()); + } + try { + Filter::bearerToken(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('bearerToken must be a string', $exception->getMessage()); + } + try { + Filter::bearerToken(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('bearerToken cannot be empty', $exception->getMessage()); + } + Filter::bearerToken('1'); + } + + public function testPublicKeyId() + { + try { + Filter::publicKeyId(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('publicKeyId must be a string', $exception->getMessage()); + } + try { + Filter::publicKeyId(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('publicKeyId must be a string', $exception->getMessage()); + } + try { + Filter::publicKeyId(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('publicKeyId cannot be empty', $exception->getMessage()); + } + Filter::publicKeyId('1'); + } + + public function testPrivateKeyFile() + { + try { + Filter::privateKeyFile(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('privateKeyFile must be a string', $exception->getMessage()); + } + try { + Filter::privateKeyFile(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('privateKeyFile must be a string', $exception->getMessage()); + } + try { + Filter::privateKeyFile(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('privateKeyFile cannot be empty', $exception->getMessage()); + } + Filter::privateKeyFile('1'); + } + + public function testRoleName() + { + Filter::roleName(null); + try { + Filter::roleName(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('roleName must be a string', $exception->getMessage()); + } + try { + Filter::roleName(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('roleName cannot be empty', $exception->getMessage()); + } + Filter::roleName('1'); + } + + public function testDisableIMDSv1() + { + try { + Filter::disableIMDSv1(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('disableIMDSv1 must be a boolean', $exception->getMessage()); + } + try { + Filter::disableIMDSv1(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('disableIMDSv1 must be a boolean', $exception->getMessage()); + } + try { + Filter::disableIMDSv1(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('disableIMDSv1 must be a boolean', $exception->getMessage()); + } + Filter::disableIMDSv1(true); + } + + public function testRoleArn() + { + try { + Filter::roleArn(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('roleArn cannot be empty', $exception->getMessage()); + } + try { + Filter::roleArn(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('roleArn cannot be empty', $exception->getMessage()); + } + Filter::roleArn('1'); + } + + public function testOidcProviderArn() + { + try { + Filter::oidcProviderArn(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('oidcProviderArn cannot be empty', $exception->getMessage()); + } + try { + Filter::oidcProviderArn(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('oidcProviderArn cannot be empty', $exception->getMessage()); + } + Filter::roleName('1'); + } + + public function testOidcTokenFilePath() + { + try { + Filter::oidcTokenFilePath(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('oidcTokenFilePath cannot be empty', $exception->getMessage()); + } + try { + Filter::oidcTokenFilePath(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('oidcTokenFilePath cannot be empty', $exception->getMessage()); + } + Filter::oidcTokenFilePath('1'); + } + + public function testSecurityToken() + { + try { + Filter::securityToken(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('securityToken must be a string', $exception->getMessage()); + } + try { + Filter::securityToken(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('securityToken must be a string', $exception->getMessage()); + } + try { + Filter::securityToken(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('securityToken cannot be empty', $exception->getMessage()); + } + Filter::securityToken('1'); + } + + public function testExpiration() + { + try { + Filter::expiration(true); + } catch (InvalidArgumentException $exception) { + self::assertEquals('expiration must be a int', $exception->getMessage()); + } + try { + Filter::expiration(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('expiration must be a int', $exception->getMessage()); + } + Filter::expiration(1); + } + + public function testCredentialsURI() + { + try { + Filter::credentialsURI(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('credentialsURI must be a string', $exception->getMessage()); + } + try { + Filter::credentialsURI(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('credentialsURI must be a string', $exception->getMessage()); + } + try { + Filter::credentialsURI(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('credentialsURI cannot be empty', $exception->getMessage()); + } + Filter::credentialsURI('1'); + } + + public function testReuseLastProviderEnabled() + { + try { + Filter::reuseLastProviderEnabled(null); + } catch (InvalidArgumentException $exception) { + self::assertEquals('reuseLastProviderEnabled must be a boolean', $exception->getMessage()); + } + try { + Filter::reuseLastProviderEnabled(1); + } catch (InvalidArgumentException $exception) { + self::assertEquals('reuseLastProviderEnabled must be a boolean', $exception->getMessage()); + } + try { + Filter::reuseLastProviderEnabled(''); + } catch (InvalidArgumentException $exception) { + self::assertEquals('reuseLastProviderEnabled must be a boolean', $exception->getMessage()); + } + Filter::reuseLastProviderEnabled(true); + } +} diff --git a/tests/Unit/HelperTest.php b/tests/Unit/Utils/HelperTest.php similarity index 77% rename from tests/Unit/HelperTest.php rename to tests/Unit/Utils/HelperTest.php index 3189036..bef8b55 100644 --- a/tests/Unit/HelperTest.php +++ b/tests/Unit/Utils/HelperTest.php @@ -2,7 +2,8 @@ namespace AlibabaCloud\Credentials\Tests\Unit; -use AlibabaCloud\Credentials\Helper; +use AlibabaCloud\Credentials\Credential; +use AlibabaCloud\Credentials\Utils\Helper; use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionException; @@ -85,7 +86,7 @@ public static function testInOpenBaseDir() ini_set('open_basedir', $dirs); self::assertEquals($dirs, ini_get('open_basedir')); } else { - $dirs = 'vfs://AlibabaCloud:/home:/Users:/private:/a/b:/d'; + $dirs = 'vfs://AlibabaCloud:/home:/Users:/private:/a/b'; ini_set('open_basedir', $dirs); self::assertEquals($dirs, ini_get('open_basedir')); self::assertTrue(Helper::inOpenBasedir('/Users/alibabacloud')); @@ -99,6 +100,7 @@ public static function testInOpenBaseDir() self::assertFalse(Helper::inOpenBasedir('/x/d/c.txt')); self::assertFalse(Helper::inOpenBasedir('/a/b.php')); } + ini_set('open_basedir', null); } public function testMerge() @@ -115,10 +117,10 @@ public function testMerge() self::assertEquals( [ - 0 => 'abc', - 1 => 'a', - 2 => 'b', - 3 => [ + 0 => 'abc', + 1 => 'a', + 2 => 'b', + 3 => [ 0 => 'c', 1 => 'd', ], @@ -141,7 +143,7 @@ public function testGetsHomeDirectoryForWindowsUser() putenv('HOME='); putenv('HOMEDRIVE=C:'); putenv('HOMEPATH=\\Users\\Alibaba'); - $ref = new ReflectionClass(Helper::class); + $ref = new ReflectionClass(Helper::class); $method = $ref->getMethod('getHomeDirectory'); $method->setAccessible(true); $this->assertEquals('C:\\Users\\Alibaba', $method->invoke(null)); @@ -156,9 +158,35 @@ public function testGetsHomeDirectoryForLinuxUser() putenv('HOME=/root'); putenv('HOMEDRIVE='); putenv('HOMEPATH='); - $ref = new ReflectionClass(Helper::class); + $ref = new ReflectionClass(Helper::class); $method = $ref->getMethod('getHomeDirectory'); $method->setAccessible(true); $this->assertEquals('/root', $method->invoke(null)); } + + public function testSnakeToCamelCase() + { + self::assertEquals('', Helper::snakeToCamelCase('')); + self::assertEquals('bearerToken', Helper::snakeToCamelCase('bearer_token')); + // take care + self::assertEquals('disableImdsV1', Helper::snakeToCamelCase('disable_imds_v1')); + self::assertEquals('publicKeyId', Helper::snakeToCamelCase('public_key_id')); + self::assertEquals('accessKeyId', Helper::snakeToCamelCase('access_key_id')); + } + + public function testGetUserAgent() + { + self::assertStringStartsWith('AlibabaCloud', Helper::getUserAgent()); + self::assertStringEndsWith('Credentials/' . Credential::VERSION . ' TeaDSL/1', Helper::getUserAgent()); + } + + public function testUnsetReturnNull() { + $params = [ + 'key' => 'value', + 'test' => '', + ]; + self::assertEquals('value', Helper::unsetReturnNull($params, 'key')); + self::assertEquals('', Helper::unsetReturnNull($params, 'test')); + self::assertNull(Helper::unsetReturnNull($params, 'access_key_id')); + } } diff --git a/tests/Unit/MockTraitTest.php b/tests/Unit/Utils/MockTraitTest.php similarity index 98% rename from tests/Unit/MockTraitTest.php rename to tests/Unit/Utils/MockTraitTest.php index 0cc6d13..49178b3 100644 --- a/tests/Unit/MockTraitTest.php +++ b/tests/Unit/Utils/MockTraitTest.php @@ -33,7 +33,7 @@ public function testRequestException() $credential = new RamRoleArnCredential([ 'access_key_id' => 'access_key_id', 'access_key_secret' => 'access_key_secret', - 'role_arn' => 'role_arn', + 'role_arn' => 'role_arn_mock', 'role_session_name' => 'role_session_name', 'policy' => [], ]);