From 1d6a525ffca60f95744020421ac136e274a08fbd Mon Sep 17 00:00:00 2001 From: yndu13 Date: Thu, 29 Aug 2024 17:06:33 +0800 Subject: [PATCH] refactor: improve all credentials providers --- .github/workflows/ci.yml | 2 +- .phpunit.result.cache | 1 + README-zh-CN.md | 1 - README.md | 1 - src/AccessKeyCredential.php | 25 +- src/BearerTokenCredential.php | 13 +- src/Credential.php | 273 +++++++++----- src/Credential/Config.php | 266 ++++++++++++-- src/Credential/CredentialModel.php | 125 +++++++ src/Credentials.php | 4 +- src/CredentialsInterface.php | 11 +- src/CredentialsProviderWrap.php | 62 ++++ src/EcsRamRoleCredential.php | 46 ++- src/Filter.php | 151 -------- .../CLIProfileCredentialsProvider.php | 180 ++++++++++ src/Providers/ChainProvider.php | 5 +- src/Providers/Credentials.php | 87 +++++ src/Providers/CredentialsProvider.php | 24 ++ src/Providers/DefaultCredentialsProvider.php | 176 ++++++++++ .../EcsRamRoleCredentialsProvider.php | 250 +++++++++++++ src/Providers/EcsRamRoleProvider.php | 180 ---------- ...EnvironmentVariableCredentialsProvider.php | 61 ++++ .../OIDCRoleArnCredentialsProvider.php | 260 ++++++++++++++ src/Providers/ProfileCredentialsProvider.php | 183 ++++++++++ src/Providers/Provider.php | 82 ----- .../RamRoleArnCredentialsProvider.php | 292 +++++++++++++++ src/Providers/RamRoleArnProvider.php | 49 --- .../RsaKeyPairCredentialsProvider.php | 196 +++++++++++ src/Providers/RsaKeyPairProvider.php | 53 --- src/Providers/SessionCredentialsProvider.php | 86 +++++ src/Providers/StaticAKCredentialsProvider.php | 78 ++++ .../StaticSTSCredentialsProvider.php | 92 +++++ src/Providers/URLCredentialsProvider.php | 119 +++++++ src/RamRoleArnCredential.php | 46 ++- src/Request/AssumeRole.php | 37 -- src/Request/GenerateSessionAccessKey.php | 33 -- src/Request/Request.php | 116 +++--- src/RsaKeyPairCredential.php | 45 ++- src/Signature/BearerTokenSignature.php | 47 --- src/Signature/ShaHmac1Signature.php | 47 --- src/Signature/ShaHmac256Signature.php | 47 --- src/Signature/ShaHmac256WithRsaSignature.php | 64 ---- src/Signature/SignatureInterface.php | 34 -- src/StsCredential.php | 22 +- src/Utils/Filter.php | 228 ++++++++++++ src/{ => Utils}/Helper.php | 32 +- src/{ => Utils}/MockTrait.php | 2 +- tests/Feature/CredentialTest.php | 58 +-- tests/Unit/AccessKeyCredentialTest.php | 18 +- tests/Unit/BearerTokenCredentialTest.php | 10 +- tests/Unit/ChainProviderTest.php | 8 +- tests/Unit/CredentialTest.php | 14 +- tests/Unit/CredentialsTest.php | 5 +- tests/Unit/EcsRamRoleCredentialTest.php | 2 - tests/Unit/FilterTest.php | 55 --- .../Unit/Ini/VirtualRsaKeyPairCredential.php | 2 +- .../DefaultCredentialsProviderTest.php | 72 ++++ .../EcsRamRoleCredentialsProviderTest.php} | 33 +- .../ProfileCredentialsProviderTest.php | 90 +++++ .../RamRoleArnCredentialsProviderTest.php | 181 ++++++++++ tests/Unit/RamRoleArnCredentialTest.php | 17 +- tests/Unit/RsaKeyPairCredentialTest.php | 19 +- tests/Unit/StsCredentialTest.php | 2 - tests/Unit/Utils/FilterTest.php | 332 ++++++++++++++++++ tests/Unit/{ => Utils}/HelperTest.php | 31 +- tests/Unit/{ => Utils}/MockTraitTest.php | 0 66 files changed, 3951 insertions(+), 1232 deletions(-) create mode 100644 .phpunit.result.cache create mode 100644 src/Credential/CredentialModel.php create mode 100644 src/CredentialsProviderWrap.php delete mode 100644 src/Filter.php create mode 100644 src/Providers/CLIProfileCredentialsProvider.php create mode 100644 src/Providers/Credentials.php create mode 100644 src/Providers/CredentialsProvider.php create mode 100644 src/Providers/DefaultCredentialsProvider.php create mode 100644 src/Providers/EcsRamRoleCredentialsProvider.php delete mode 100644 src/Providers/EcsRamRoleProvider.php create mode 100644 src/Providers/EnvironmentVariableCredentialsProvider.php create mode 100644 src/Providers/OIDCRoleArnCredentialsProvider.php create mode 100644 src/Providers/ProfileCredentialsProvider.php delete mode 100644 src/Providers/Provider.php create mode 100644 src/Providers/RamRoleArnCredentialsProvider.php delete mode 100644 src/Providers/RamRoleArnProvider.php create mode 100644 src/Providers/RsaKeyPairCredentialsProvider.php delete mode 100644 src/Providers/RsaKeyPairProvider.php create mode 100644 src/Providers/SessionCredentialsProvider.php create mode 100644 src/Providers/StaticAKCredentialsProvider.php create mode 100644 src/Providers/StaticSTSCredentialsProvider.php create mode 100644 src/Providers/URLCredentialsProvider.php delete mode 100644 src/Request/AssumeRole.php delete mode 100644 src/Request/GenerateSessionAccessKey.php delete mode 100644 src/Signature/BearerTokenSignature.php delete mode 100644 src/Signature/ShaHmac1Signature.php delete mode 100644 src/Signature/ShaHmac256Signature.php delete mode 100644 src/Signature/ShaHmac256WithRsaSignature.php delete mode 100644 src/Signature/SignatureInterface.php create mode 100644 src/Utils/Filter.php rename src/{ => Utils}/Helper.php (83%) rename src/{ => Utils}/MockTrait.php (98%) delete mode 100644 tests/Unit/FilterTest.php create mode 100644 tests/Unit/Providers/DefaultCredentialsProviderTest.php rename tests/Unit/{EcsRamRoleProviderTest.php => Providers/EcsRamRoleCredentialsProviderTest.php} (84%) create mode 100644 tests/Unit/Providers/ProfileCredentialsProviderTest.php create mode 100644 tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php create mode 100644 tests/Unit/Utils/FilterTest.php rename tests/Unit/{ => Utils}/HelperTest.php (82%) rename tests/Unit/{ => Utils}/MockTraitTest.php (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2c0fe5..54a9ab8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..e6a0b76 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +{"version":1,"defects":{"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testRamRoleArnCredential":4,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testAccessKey":4,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testEcsRamRoleCredential":3,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testRsaKeyPairCredential":3,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testSTS":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeyIdEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeyIdFormat":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeySecretEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeySecretFormat":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #0":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #1":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #2":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #3":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #4":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #5":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #6":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #7":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #8":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #9":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #10":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #11":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #13":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #14":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #17":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #18":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #19":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #20":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #21":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #22":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #23":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #24":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testRamRoleArnCredential":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testAccessKey":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testGet":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testGetNotFound":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testLoad":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefault":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefault500":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefaultEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testSts":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testStsIncomplete":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testSts500":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testRoleNameEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testStsWithoutMock":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testFlushCustomProviders":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testDefaultProvider":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testConstruct":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testgetDisableECSIMDSv1":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testRefreshMetadataTokenDefault":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testDefault404":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testEnableV1404":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testNeedToRefresh":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIni":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIniEmpty":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIniWithDIYFile":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testInOpenBaseDir":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultProvider":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetEnv":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetInstance":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultFile":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultName":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testSts":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testStsIncomplete":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testAccessKeyIdEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testSts":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testStsIncomplete":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testAccessKeyIdEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testNotFoundFile":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testOpenBasedirException":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testConstruct":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testSts":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testStsIncomplete":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPublicKeyIdEmpty":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPublicKeyIdFormat":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPrivateKeyFileEmpty":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPrivateKeyFileFormat":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\BearerTokenSignatureTest::testBearerTokenSignature":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac1SignatureTest::testShaHmac1Signature":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256SignatureTest::testShaHmac256Signature":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256WithRsaSignatureTest::testShaHmac256Signature":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256WithRsaSignatureTest::testShaHmac256SignatureBadPrivateKey":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeyIdEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeyIdFormat":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeySecretEmpty":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeySecretFormat":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #0":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #1":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #2":5,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testInOpenBaseDir":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testGetsHomeDirectoryForLinuxUser":1,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testSnakeToCamelCase":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testGetUserAgent":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testCredentialName":4,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testRoleArn":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testExpiration":3,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetIniEmpty":6,"AlibabaCloud\\Credentials\\Tests\\Unit\\CredentialsTest::testALL":4},"times":{"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testRamRoleArnCredential":0.58,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testAccessKey":0.006,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testEcsRamRoleCredential":5.037,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testRsaKeyPairCredential":0.605,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testSTS":0.008,"AlibabaCloud\\Credentials\\Tests\\Feature\\CredentialTest::testBearerToken":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testConstruct":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeyIdEmpty":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeyIdFormat":0.01,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeySecretEmpty":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\AccessKeyCredentialTest::testAccessKeySecretFormat":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\BearerTokenCredentialTest::testBearerTokenEmpty":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\BearerTokenCredentialTest::testBearerTokenFormat":0.003,"AlibabaCloud\\Credentials\\Tests\\Unit\\BearerTokenCredentialTest::testConstruct":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testLoad":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #0":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #1":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #2":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #3":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #4":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #5":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #6":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #7":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #8":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #9":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #10":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #11":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #12":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #13":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #14":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #15":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #16":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #17":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #18":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #19":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #20":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #21":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #22":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #23":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testException with data set #24":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testRamRoleArnCredential":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialTest::testAccessKey":0.008,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testGet":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testGetNotFound":0.007,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\CredentialsTest::testLoad":0.007,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testConstruct":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testConstructWithIMDSv2":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefault":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefault404":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefault500":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testDefaultEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testSts":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testStsIncomplete":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testSts404":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testSts500":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testRoleNameEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialTest::testStsWithoutMock":5.008,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testNoCustomProviders":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testFlushCustomProviders":0.003,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testDefaultProvider":0.007,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testConstruct":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testgetDisableECSIMDSv1":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testRefreshMetadataTokenDefault":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testDefault404":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testEnableV1404":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\EcsRamRoleCredentialsProviderTest::testNeedToRefresh":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIni":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIniEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetIniWithDIYFile":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testInOpenBaseDir":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultProvider":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetEnv":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testSetInstance":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultFile":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ProfileCredentialsProviderTest::testDefaultName":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testConstruct":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testSts":0.003,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testStsIncomplete":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testAccessKeyIdEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialsProviderTest::testAccessKeyIdFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testConstruct":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testSts":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testStsIncomplete":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testAccessKeyIdEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RamRoleArnCredentialTest::testAccessKeyIdFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testNotFoundFile":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testOpenBasedirException":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testConstruct":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testSts":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testStsIncomplete":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPublicKeyIdEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPublicKeyIdFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPrivateKeyFileEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\RsaKeyPairCredentialTest::testPrivateKeyFileFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\BearerTokenSignatureTest::testBearerTokenSignature":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac1SignatureTest::testShaHmac1Signature":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256SignatureTest::testShaHmac256Signature":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256WithRsaSignatureTest::testShaHmac256Signature":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Signature\\ShaHmac256WithRsaSignatureTest::testShaHmac256SignatureBadPrivateKey":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testConstruct":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeyIdEmpty":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeyIdFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeySecretEmpty":0.001,"AlibabaCloud\\Credentials\\Tests\\Unit\\StsCredentialTest::testAccessKeySecretFormat":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #0":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #1":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #2":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testDefault":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testEnv":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testSwitch":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testString":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testEnvNotEmpty":0.003,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testEnvNotEmptyException":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testInOpenBaseDir":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testMerge":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testGetsHomeDirectoryForWindowsUser":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testGetsHomeDirectoryForLinuxUser":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\MockTraitTest::testRequestException":0.021,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\DefaultCredentialsProviderTest::testGetProviderName":0.003,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testAccessKey with data set #3":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testSnakeToCamelCase":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\HelperTest::testGetUserAgent":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testTimeout with data set #0":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testTimeout with data set #1":0,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testCredentialName":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testBearerToken":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testDisableIMDSv1":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testRoleName":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testPrivateKeyFile":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testPublicKeyId":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testRoleArn":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testOidcTokenFilePath":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testSecurityToken":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testExpiration":0.005,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testCredentialsURI":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\FilterTest::testReuseLastProviderEnabled":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetIni":0.006,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetIniEmpty":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetIniWithDIYFile":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testInOpenBaseDir":0.004,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testDefaultProvider":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetEnv":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testSetInstance":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testDefaultFile":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\Filter\\ChainProviderTest::testDefaultName":0.002,"AlibabaCloud\\Credentials\\Tests\\Unit\\CredentialsTest::testALL":0.006}} \ No newline at end of file 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/src/AccessKeyCredential.php b/src/AccessKeyCredential.php index 6d7d7c9..f9579d3 100644 --- a/src/AccessKeyCredential.php +++ b/src/AccessKeyCredential.php @@ -2,9 +2,11 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Credential\CredentialModel; /** + * @deprecated * Use the AccessKey to complete the authentication. */ class AccessKeyCredential implements CredentialsInterface @@ -29,7 +31,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; } @@ -57,16 +59,19 @@ public function __toString() return "$this->accessKeyId#$this->accessKeySecret"; } - /** - * @return ShaHmac1Signature - */ - public function getSignature() - { - return new ShaHmac1Signature(); - } - 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..dcac7cd 100644 --- a/src/BearerTokenCredential.php +++ b/src/BearerTokenCredential.php @@ -2,7 +2,8 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Signature\BearerTokenSignature; +use AlibabaCloud\Credentials\Utils\Filter; +use AlibabaCloud\Credentials\Credential\CredentialModel; /** * Class BearerTokenCredential @@ -44,10 +45,14 @@ public function __toString() } /** - * @return BearerTokenSignature + * @inheritDoc */ - public function getSignature() + public function getCredential() { - return new BearerTokenSignature(); + 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..3439708 100644 --- a/src/CredentialsInterface.php +++ b/src/CredentialsInterface.php @@ -2,22 +2,19 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Signature\SignatureInterface; +use AlibabaCloud\Credentials\Credential\CredentialModel; /** + * @internal This class is intended for internal use within the package. * Interface CredentialsInterface * * @codeCoverageIgnore */ interface CredentialsInterface { - /** - * @return string - */ - public function __toString(); /** - * @return SignatureInterface + * @return CredentialModel */ - public function getSignature(); + public function getCredential(); } diff --git a/src/CredentialsProviderWrap.php b/src/CredentialsProviderWrap.php new file mode 100644 index 0000000..939d1f9 --- /dev/null +++ b/src/CredentialsProviderWrap.php @@ -0,0 +1,62 @@ +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); + } +} \ No newline at end of file diff --git a/src/EcsRamRoleCredential.php b/src/EcsRamRoleCredential.php index 8662f77..71f65ec 100644 --- a/src/EcsRamRoleCredential.php +++ b/src/EcsRamRoleCredential.php @@ -2,15 +2,18 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\EcsRamRoleProvider; +use AlibabaCloud\Credentials\Providers\Credentials; +use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; use AlibabaCloud\Credentials\Request\Request; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +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'); } @@ -110,14 +111,6 @@ public function __toString() return "roleName#$this->roleName"; } - /** - * @return ShaHmac1Signature - */ - public function getSignature() - { - return new ShaHmac1Signature(); - } - /** * @return string * @throws Exception @@ -129,17 +122,18 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return 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 +174,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 + */ + 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); + } + + $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 ($profile['name'] === $profileName) { + switch ($profile['mode']) { + case 'AK': + return new StaticAKCredentialsProvider([ + 'accessKeyId' => $profile['access_key_id'], + 'accessKeySecret' => $profile['access_key_secret'], + ]); + case 'RamRoleArn': + $innerProvider = new StaticAKCredentialsProvider([ + 'accessKeyId' => $profile['access_key_id'], + 'accessKeySecret' => $profile['access_key_secret'], + ]); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $innerProvider, + 'roleArn' => $profile['ram_role_arn'], + 'roleSessionName' => $profile['ram_session_name'], + 'durationSeconds' => $profile['expired_seconds'], + 'stsRegionId' => $profile['sts_region'], + ]); + case 'EcsRamRole': + return new EcsRamRoleCredentialsProvider([ + 'roleName' => $profile['ram_role_name'], + ]); + case 'OIDC': + return new OIDCRoleArnCredentialsProvider([ + 'roleArn' => $profile['ram_role_arn'], + 'oidcProviderArn' => $profile['oidc_provider_arn'], + 'oidcTokenFilePath' => $profile['oidc_token_file'], + 'roleSessionName' => $profile['ram_session_name'], + 'durationSeconds' => $profile['expired_seconds'], + 'stsRegionId' => $profile['sts_region'], + ]); + case 'ChainableRamRoleArn': + $previousProvider = $this->reloadCredentialsProvider($profileFile, $profile['source_profile']); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $previousProvider, + 'roleArn' => $profile['ram_role_arn'], + 'roleSessionName' => $profile['ram_session_name'], + 'durationSeconds' => $profile['expired_seconds'], + 'stsRegionId' => $profile['sts_region'], + ]); + default: + throw new RuntimeException('Unsupported credential mode form CLI credentials file: ' . $profile['mode']); + } + } + } + } + } + throw new RuntimeException('Failed to get credential form 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_PROFILE') === 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 && $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(); + 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..f5ea5b5 --- /dev/null +++ b/src/Providers/EcsRamRoleCredentialsProvider.php @@ -0,0 +1,250 @@ +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']; + } + } + + 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() + { + if (is_null($this->roleName) || $this->roleName === '') { + $this->roleName = $this->getRoleNameFromMeta(); + } + $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; + + $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..80c0c73 --- /dev/null +++ b/src/Providers/OIDCRoleArnCredentialsProvider.php @@ -0,0 +1,260 @@ +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'); + + $bodyForm = []; + $bodyForm['RoleArn'] = $this->roleArn; + $bodyForm['OIDCProviderArn'] = $this->oidcProviderArn; + try { + $oidcToken = file_get_contents($this->oidcTokenFilePath); + $bodyForm['OIDCToken'] = $oidcToken; + } catch (Exception $exception) { + throw new InvalidArgumentException($exception->getMessage()); + } + $bodyForm['RoleSessionName'] = $this->roleSessionName; + $bodyForm['DurationSeconds'] = (string) $this->durationSeconds; + if (!is_null($this->policy)) { + $bodyForm['Policy'] = $this->policy; + } + $options['body'] = str_replace('+', '%20', http_build_query($bodyForm)); + + $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..28d7194 --- /dev/null +++ b/src/Providers/ProfileCredentialsProvider.php @@ -0,0 +1,183 @@ +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 ($credentialsConfigures['type']) { + case 'access_key': + return new StaticAKCredentialsProvider([ + 'accessKeyId' => $credentialsConfigures['access_key_id'], + 'accessKeySecret' => $credentialsConfigures['access_key_secret'], + ]); + case 'ram_role_arn': + $innerProvider = new StaticAKCredentialsProvider([ + 'accessKeyId' => $credentialsConfigures['access_key_id'], + 'accessKeySecret' => $credentialsConfigures['access_key_secret'], + ]); + return new RamRoleArnCredentialsProvider([ + 'credentialsProvider' => $innerProvider, + 'roleArn' => $credentialsConfigures['role_arn'], + 'roleSessionName' => $credentialsConfigures['role_session_name'], + 'policy' => $credentialsConfigures['policy'], + ]); + case 'ecs_ram_role': + return new EcsRamRoleCredentialsProvider([ + 'roleName' => $credentialsConfigures['role_name'], + ]); + case 'oidc_role_arn': + return new OIDCRoleArnCredentialsProvider([ + 'roleArn' => $credentialsConfigures['role_arn'], + 'oidcProviderArn' => $credentialsConfigures['oidc_provider_arn'], + 'oidcTokenFilePath' => $credentialsConfigures['oidc_token_file_path'], + 'roleSessionName' => $credentialsConfigures['role_session_name'], + 'policy' => $credentialsConfigures['policy'], + ]); + default: + throw new RuntimeException('Unsupported credential type form credentials file: ' . $credentialsConfigures['type']); + } + } + } + throw new RuntimeException('Failed to get credential form 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..71db052 --- /dev/null +++ b/src/Providers/RamRoleArnCredentialsProvider.php @@ -0,0 +1,292 @@ +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; + } +} 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..18c5ecd --- /dev/null +++ b/src/Providers/RsaKeyPairCredentialsProvider.php @@ -0,0 +1,196 @@ +filterOptions($options); + $this->filterDurationSeconds($params); + $this->filterSTSEndpoint($params); + $this->publicKeyId = $params['publicKeyId']; + $privateKeyFile = $params['privateKeyFile']; + 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 (Helper::envNotEmpty('ALIBABA_CLOUD_STS_REGION')) { + $this->stsEndpoint = 'sts' . Helper::env('ALIBABA_CLOUD_STS_REGION') . '.aliyuncs.com'; + } + + 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']; + + + 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..fa61698 --- /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' => $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..9f638c9 100644 --- a/src/RamRoleArnCredential.php +++ b/src/RamRoleArnCredential.php @@ -2,13 +2,16 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\RamRoleArnProvider; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Providers\Credentials; +use AlibabaCloud\Credentials\Providers\RamRoleArnCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; +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']; } @@ -142,14 +145,6 @@ public function __toString() return "$this->accessKeyId#$this->accessKeySecret#$this->roleArn#$this->roleSessionName"; } - /** - * @return ShaHmac1Signature - */ - public function getSignature() - { - return new ShaHmac1Signature(); - } - /** * @return string */ @@ -177,13 +172,20 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return 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 +217,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..1226c26 100644 --- a/src/RsaKeyPairCredential.php +++ b/src/RsaKeyPairCredential.php @@ -2,13 +2,16 @@ namespace AlibabaCloud\Credentials; -use AlibabaCloud\Credentials\Providers\RsaKeyPairProvider; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Providers\Credentials; +use AlibabaCloud\Credentials\Providers\RsaKeyPairCredentialsProvider; +use AlibabaCloud\Credentials\Credential\CredentialModel; +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) { @@ -98,14 +107,6 @@ public function __toString() return "publicKeyId#$this->publicKeyId"; } - /** - * @return ShaHmac1Signature - */ - public function getSignature() - { - return new ShaHmac1Signature(); - } - /** * @return string * @throws Exception @@ -117,13 +118,17 @@ public function getAccessKeyId() } /** - * @return StsCredential + * @return 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 +160,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/Signature/BearerTokenSignature.php b/src/Signature/BearerTokenSignature.php deleted file mode 100644 index 1d67a80..0000000 --- a/src/Signature/BearerTokenSignature.php +++ /dev/null @@ -1,47 +0,0 @@ -getMessage() - ); - } - - return base64_encode($binarySignature); - } -} diff --git a/src/Signature/SignatureInterface.php b/src/Signature/SignatureInterface.php deleted file mode 100644 index 9dfb73b..0000000 --- a/src/Signature/SignatureInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -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; } /** @@ -89,10 +91,16 @@ public function __toString() } /** - * @return ShaHmac1Signature + * @inheritDoc */ - public function getSignature() + public function getCredential() { - return new ShaHmac1Signature(); + 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..ba37491 --- /dev/null +++ b/src/Utils/Filter.php @@ -0,0 +1,228 @@ +assertEquals('foo', $credential->getAccessKeyId()); $this->assertEquals('bar', $credential->getAccessKeySecret()); $this->assertEquals('access_key', $credential->getType()); + + $result = $credential->getCredential(); + $this->assertEquals('foo', $result->getAccessKeyId()); + $this->assertEquals('bar', $result->getAccessKeySecret()); + $this->assertEquals('access_key', $result->getType()); } /** @@ -54,15 +59,15 @@ public function testEcsRamRoleCredential() $this->expectException(\GuzzleHttp\Exception\ConnectException::class); if (method_exists($this, 'expectExceptionMessageMatches')) { - $this->expectExceptionMessageMatches('/timed/'); + $this->expectExceptionMessageMatches('/Timeout/'); } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/timed/'); + $this->expectExceptionMessageRegExp('/Timeout/'); } // Assert $this->assertEquals('foo', $credential->getRoleName()); $this->assertEquals('ecs_ram_role', $credential->getType()); - $credential->getAccessKeySecret(); + $credential->getCredential(); } /** @@ -71,7 +76,7 @@ public function testEcsRamRoleCredential() */ public function testRamRoleArnCredential() { - Credentials::cancelMock(); + Requests::cancelMock(); $config = new Credential\Config([ 'type' => 'ram_role_arn', 'accessKeyId' => Helper::envNotEmpty('ACCESS_KEY_ID'), @@ -84,20 +89,26 @@ 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->assertTrue(null !== $result->getAccessKeyId()); + $this->assertTrue(null !== $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() { - Credentials::cancelMock(); + $this->expectException(RuntimeException::class); + $reg = '/Specified access key is not found or invalid./'; + if (method_exists($this, 'expectExceptionMessageMatches')) { + $this->expectExceptionMessageMatches($reg); + } elseif (method_exists($this, 'expectExceptionMessageRegExp')) { + $this->expectExceptionMessageRegExp($reg); + } + Requests::cancelMock(); $publicKeyId = Helper::envNotEmpty('PUBLIC_KEY_ID'); $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); $config = new Credential\Config([ @@ -109,11 +120,10 @@ public function testRsaKeyPairCredential() $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->assertTrue(null !== $result->getAccessKeyId()); + $this->assertTrue(null !== $result->getAccessKeySecret()); + $this->assertEquals('rsa_key_pair', $result->getType()); } /** @@ -131,10 +141,11 @@ public function testSTS() $credential = new Credential($config); // 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->assertEquals('foo', $result->getAccessKeyId()); + $this->assertEquals('bar', $result->getAccessKeySecret()); + $this->assertEquals('token', $result->getSecurityToken()); + $this->assertEquals('sts', $result->getType()); } /** @@ -150,7 +161,8 @@ public function testBearerToken() $credential = new Credential($config); // Assert - $this->assertEquals('token', $credential->getBearerToken()); - $this->assertEquals('bearer', $credential->getType()); + $result = $credential->getCredential(); + $this->assertEquals('token', $result->getBearerToken()); + $this->assertEquals('bearer', $result->getType()); } } diff --git a/tests/Unit/AccessKeyCredentialTest.php b/tests/Unit/AccessKeyCredentialTest.php index 07dad10..4fe1e65 100644 --- a/tests/Unit/AccessKeyCredentialTest.php +++ b/tests/Unit/AccessKeyCredentialTest.php @@ -3,7 +3,6 @@ namespace AlibabaCloud\Credentials\Tests\Unit; use AlibabaCloud\Credentials\AccessKeyCredential; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use PHPUnit\Framework\TestCase; use InvalidArgumentException; @@ -26,13 +25,12 @@ public function testConstruct() // Assert $this->assertEquals($accessKeyId, $credential->getAccessKeyId()); $this->assertEquals($accessKeySecret, $credential->getAccessKeySecret()); - $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals("$accessKeyId#$accessKeySecret", (string)$credential); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage access_key_id cannot be empty + * @expectedExceptionMessage accessKeyId cannot be empty */ public function testAccessKeyIdEmpty() { @@ -41,14 +39,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 +55,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 +71,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 +79,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 +88,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..8a76c4c 100644 --- a/tests/Unit/BearerTokenCredentialTest.php +++ b/tests/Unit/BearerTokenCredentialTest.php @@ -3,7 +3,6 @@ namespace AlibabaCloud\Credentials\Tests\Unit; use AlibabaCloud\Credentials\BearerTokenCredential; -use AlibabaCloud\Credentials\Signature\BearerTokenSignature; use InvalidArgumentException; use Exception; use PHPUnit\Framework\TestCase; @@ -18,7 +17,7 @@ class BearerTokenCredentialTest extends TestCase /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage bearer_token cannot be empty + * @expectedExceptionMessage bearerToken cannot be empty */ public function testBearerTokenEmpty() { @@ -26,7 +25,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 +33,7 @@ public function testBearerTokenEmpty() /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage bearer_token must be a string + * @expectedExceptionMessage bearerToken must be a string */ public function testBearerTokenFormat() { @@ -42,7 +41,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); @@ -60,6 +59,5 @@ public function testConstruct() // Assert $this->assertEquals($bearerToken, $credential->getBearerToken()); $this->assertEquals($expected, (string)$credential); - $this->assertInstanceOf(BearerTokenSignature::class, $credential->getSignature()); } } diff --git a/tests/Unit/ChainProviderTest.php b/tests/Unit/ChainProviderTest.php index ca72caf..f791dad 100644 --- a/tests/Unit/ChainProviderTest.php +++ b/tests/Unit/ChainProviderTest.php @@ -1,8 +1,8 @@ getAccessKeyId()); self::assertEquals('bar', $credential->getAccessKeySecret()); - self::assertEquals($config, $credential->getConfig()); - self::assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); + $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()); } } diff --git a/tests/Unit/CredentialsTest.php b/tests/Unit/CredentialsTest.php index ff94133..32c6732 100644 --- a/tests/Unit/CredentialsTest.php +++ b/tests/Unit/CredentialsTest.php @@ -1,6 +1,6 @@ assertEquals($roleName, $credential->getRoleName()); - $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals($expected, (string)$credential); } 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/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/DefaultCredentialsProviderTest.php b/tests/Unit/Providers/DefaultCredentialsProviderTest.php new file mode 100644 index 0000000..9bad597 --- /dev/null +++ b/tests/Unit/Providers/DefaultCredentialsProviderTest.php @@ -0,0 +1,72 @@ +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 testDefaultProvider() + { + $provider = new DefaultCredentialsProvider(); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Unable to load credentials from any of the providers in the chain: EnvironmentVariableCredentialsProvider: Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID), CLIProfileCredentialsProvider: Credentials file is not readable: /Users/nanhe/.aliyun/config.json, ProfileCredentialsProvider: Credentials file is not readable: /Users/nanhe/.alibabacloud/credentials'); + $provider->getCredentials(); + // try { + // $provider->getCredentials(); + // self::assertTrue(true); + // } catch (RuntimeException $e) { + // self::assertEquals('Unable to load credentials from any of the providers in the chain: EnvironmentVariableCredentialsProvider: Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID), CLIProfileCredentialsProvider: Credentials file is not readable: /Users/nanhe/.aliyun/config.json, ProfileCredentialsProvider: Credentials file is not readable: /Users/nanhe/.alibabacloud/credentials', $e->getMessage()); + // } + + // $vf = VirtualAccessKeyCredential::ok(); + // putenv("ALIBABA_CLOUD_CREDENTIALS_FILE=$vf"); + // $provider->getCredentials(); + // try { + // $provider->getCredentials(); + // } catch (RuntimeException $e) { + // self::assertEquals('Unable to load credentials from any of the providers in the chain: EnvironmentVariableCredentialsProvider: Environment variable ALIBABA_CLOUD_ACCESS_KEY_ID is not set, EnvironmentVariableCredentialsProvider: Environment variable ALIBABA_CLOUD_ACCESS_KEY_SECRET is not set, EnvironmentVariableCredentialsProvider: Environment variable ALIBABA_CLOUD_SECURITY_TOKEN is not set, CLIProfileCredentialsProvider: Profile file not found, ProfileCredentialsProvider: Profile', $e->getMessage()); + + // self::assertTrue(true); + // } + + } +} diff --git a/tests/Unit/EcsRamRoleProviderTest.php b/tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php similarity index 84% rename from tests/Unit/EcsRamRoleProviderTest.php rename to tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php index ab47920..a4e2ea7 100644 --- a/tests/Unit/EcsRamRoleProviderTest.php +++ b/tests/Unit/Providers/EcsRamRoleCredentialsProviderTest.php @@ -4,8 +4,7 @@ use AlibabaCloud\Credentials\Credentials; use AlibabaCloud\Credentials\EcsRamRoleCredential; -use AlibabaCloud\Credentials\Providers\EcsRamRoleProvider; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; +use AlibabaCloud\Credentials\Providers\EcsRamRoleCredentialsProvider; use Exception; use GuzzleHttp\Exception\GuzzleException; use InvalidArgumentException; @@ -13,7 +12,7 @@ use RuntimeException; use ReflectionClass; -class EcsRamRoleProviderTest extends TestCase +class EcsRamRoleCredentialsProviderTest extends TestCase { /** @@ -40,7 +39,7 @@ public function testConstruct() // Test $credential = new EcsRamRoleCredential($roleName); - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); $sessionConfig = $this->getPrivateField($sessionCredential, 'config'); @@ -53,7 +52,7 @@ public function testConstruct() * @throws Exception */ private function invokeProtectedFunc($instance, $method) { - $reflection = new ReflectionClass(EcsRamRoleProvider::class); + $reflection = new ReflectionClass(EcsRamRoleCredentialsProvider::class); $method = $reflection->getMethod($method); $method->setAccessible(true); @@ -76,7 +75,7 @@ public function testgetDisableECSIMDSv1() // Test $credential = new EcsRamRoleCredential($roleName); - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); @@ -84,37 +83,37 @@ public function testgetDisableECSIMDSv1() 'metadataTokenDuration' => 3600, ]; - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=true'); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=true'); self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=TRUE'); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=TRUE'); self::assertEquals(true, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=ok'); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=ok'); self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=1'); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=1'); self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE=false'); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED=false'); self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); - putenv('ALIBABA_CLOUD_IMDSV1_DISABLE='); + putenv('ALIBABA_CLOUD_IMDSV1_DISABLED='); self::assertEquals(false, $this->invokeProtectedFunc($sessionCredential, 'getDisableECSIMDSv1')); } private function getPrivateField($instance, $field) { - $reflection = new ReflectionClass(EcsRamRoleProvider::class); + $reflection = new ReflectionClass(EcsRamRoleCredentialsProvider::class); $privateProperty = $reflection->getProperty($field); $privateProperty->setAccessible(true); return $privateProperty->getValue($instance); @@ -135,7 +134,7 @@ public function testRefreshMetadataTokenDefault() // Test $credential = new EcsRamRoleCredential($roleName); - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); Credentials::mockResponse(200, [], 'Token'); @@ -166,7 +165,7 @@ public function testDefault404() // Test $credential = new EcsRamRoleCredential($roleName); - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); Credentials::mockResponse(404, [], 'Error'); @@ -189,7 +188,7 @@ public function testEnableV1404() // Test $credential = new EcsRamRoleCredential($roleName); - $sessionCredential = new EcsRamRoleProvider($credential, $config); + $sessionCredential = new EcsRamRoleCredentialsProvider($credential, $config); Credentials::mockResponse(404, [], 'Error'); $token = $this->invokeProtectedFunc($sessionCredential, 'refreshMetadataToken'); diff --git a/tests/Unit/Providers/ProfileCredentialsProviderTest.php b/tests/Unit/Providers/ProfileCredentialsProviderTest.php new file mode 100644 index 0000000..7c24db0 --- /dev/null +++ b/tests/Unit/Providers/ProfileCredentialsProviderTest.php @@ -0,0 +1,90 @@ +getMessage()); + } + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Credentials file is not readable: /a/c + */ + public function testSetIniWithDIYFile() + { + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE=/a/c'); + } + + public function testInOpenBaseDir() + { + if (!Helper::isWindows()) { + $dirs = 'vfs://AlibabaCloud:/home:/Users:/private:/a/b:/d'; + } else { + $dirs = 'C:\\projects;C:\\Users'; + } + + putenv('ALIBABA_CLOUD_CREDENTIALS_FILE=/a/c'); + ini_set('open_basedir', $dirs); + self::assertEquals($dirs, ini_get('open_basedir')); + } + + public function testDefaultFile() + { + self::assertStringEndsWith( + 'credentials', + ChainProvider::getDefaultFile() + ); + putenv('ALIBABA_CLOUD_PROFILE=default'); + } + + public function testDefaultName() + { + putenv('ALIBABA_CLOUD_PROFILE=default1'); + self::assertEquals( + 'default1', + ChainProvider::getDefaultName() + ); + + putenv('ALIBABA_CLOUD_PROFILE=null'); + self::assertEquals( + 'default', + ChainProvider::getDefaultName() + ); + } + + /** + * @before + */ + protected function initialize() + { + parent::setUp(); + putenv('ALIBABA_CLOUD_ACCESS_KEY_ID=foo'); + putenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET=bar'); + } +} diff --git a/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php b/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php new file mode 100644 index 0000000..ba73bb2 --- /dev/null +++ b/tests/Unit/Providers/RamRoleArnCredentialsProviderTest.php @@ -0,0 +1,181 @@ + 'access_key_id', + 'access_key_secret' => 'access_key_secret', + 'role_arn' => 'role_arn', + 'role_session_name' => 'role_session_name', + 'policy' => '', + ]); + + // Assert + $this->assertEquals($arn, $credential->getRoleArn()); + $this->assertEquals($sessionName, $credential->getRoleSessionName()); + $this->assertEquals($policy, $credential->getPolicy()); + $this->assertEquals( + "$accessKeyId#$accessKeySecret#$arn#$sessionName", + (string)$credential + ); + + $this->assertEquals( + [], + $credential->getConfig() + ); + } + + /** + * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function testSts() + { + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeySecret": "********************", + "AccessKeyId": "STS.**************", + "Expiration": "2020-02-25T03:56:19Z", + "SecurityToken": "**************" + } +}'; + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + $credential = new RamRoleArnCredential([ + 'access_key_id' => 'access_key_id', + 'access_key_secret' => 'access_key_secret', + 'role_arn' => 'role_arn', + 'role_session_name' => 'role_session_name', + 'policy' => [], + ]); + + self::assertEquals('STS.**************', $credential->getAccessKeyId()); + self::assertEquals('********************', $credential->getAccessKeySecret()); + self::assertEquals('**************', $credential->getSecurityToken()); + self::assertEquals(strtotime('2020-02-25T03:56:19Z'), $credential->getExpiration()); + } + + /** + * @throws Exception + * @throws \GuzzleHttp\Exception\GuzzleException + * @expectedException \RuntimeException + * @expectedExceptionMessage Result contains no credentials + */ + public function testStsIncomplete() + { + // Setup + Requests::cancelMock(); + $result = '{ + "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", + "AssumedRoleUser": { + "AssumedRoleId": "********************", + "Arn": "********************" + }, + "Credentials": { + "AccessKeyId": "STS.**************", + "Expiration": "2020-02-25T03:56:19Z", + "SecurityToken": "**************" + } +}'; + Requests::mockResponse(200, [], $result); + $credential = new RamRoleArnCredential([ + 'access_key_id' => 'access_key_id2', + 'access_key_secret' => 'access_key_secret2', + 'role_arn' => 'role_arn2', + 'role_session_name' => 'role_session_name2', + 'policy' => '', + ]); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Result contains no credentials'); + // Test + self::assertEquals('TMPSK.**************', $credential->getAccessKeyId()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage access_key_id cannot be empty + */ + public function testAccessKeyIdEmpty() + { + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('access_key_id cannot be empty'); + // Test + new RamRoleArnCredential([ + 'access_key_id' => '', + 'access_key_secret' => 'access_key_secret', + 'role_arn' => 'role_arn', + 'role_session_name' => 'role_session_name', + 'policy' => '', + ]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing required access_key_secret option in config for ram_role_arn + */ + public function testAccessKeyIdFormat() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Missing required access_key_secret option in config for ram_role_arn'); + // Test + new RamRoleArnCredential([ + 'access_key_id' => 'access_key_id', + 'access_key_secret' => null, + 'role_arn' => 'role_arn', + 'role_session_name' => 'role_session_name', + 'policy' => '', + ]); + } + + /** + * @before + */ + protected function initialize() + { + // Setup + Requests::cancelMock(); + $this->credential = new RamRoleArnCredential([ + 'access_key_id' => 'access_key_id', + 'access_key_secret' => 'access_key_secret', + 'role_arn' => 'role_arn', + 'role_session_name' => 'role_session_name', + 'policy' => '', + ]); + } +} diff --git a/tests/Unit/RamRoleArnCredentialTest.php b/tests/Unit/RamRoleArnCredentialTest.php index 789d4da..5405fdf 100644 --- a/tests/Unit/RamRoleArnCredentialTest.php +++ b/tests/Unit/RamRoleArnCredentialTest.php @@ -3,8 +3,8 @@ namespace AlibabaCloud\Credentials\Tests\Unit; use AlibabaCloud\Credentials\Credentials; +use AlibabaCloud\Credentials\Request\Request as Requests; use AlibabaCloud\Credentials\RamRoleArnCredential; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use Exception; use RuntimeException; use InvalidArgumentException; @@ -42,7 +42,6 @@ public function testConstruct() $this->assertEquals($arn, $credential->getRoleArn()); $this->assertEquals($sessionName, $credential->getRoleSessionName()); $this->assertEquals($policy, $credential->getPolicy()); - $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals( "$accessKeyId#$accessKeySecret#$arn#$sessionName", (string)$credential @@ -73,10 +72,10 @@ public function testSts() "SecurityToken": "**************" } }'; - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); $credential = new RamRoleArnCredential([ 'access_key_id' => 'access_key_id', 'access_key_secret' => 'access_key_secret', @@ -100,7 +99,7 @@ public function testSts() public function testStsIncomplete() { // Setup - Credentials::cancelMock(); + Requests::cancelMock(); $result = '{ "RequestId": "88FEA385-EF5D-4A8A-8C00-A07DAE3BFD44", "AssumedRoleUser": { @@ -113,7 +112,7 @@ public function testStsIncomplete() "SecurityToken": "**************" } }'; - Credentials::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); $credential = new RamRoleArnCredential([ 'access_key_id' => 'access_key_id2', 'access_key_secret' => 'access_key_secret2', @@ -170,7 +169,7 @@ public function testAccessKeyIdFormat() protected function initialize() { // Setup - Credentials::cancelMock(); + Requests::cancelMock(); $this->credential = new RamRoleArnCredential([ 'access_key_id' => 'access_key_id', 'access_key_secret' => 'access_key_secret', diff --git a/tests/Unit/RsaKeyPairCredentialTest.php b/tests/Unit/RsaKeyPairCredentialTest.php index 55cb084..b0c1883 100644 --- a/tests/Unit/RsaKeyPairCredentialTest.php +++ b/tests/Unit/RsaKeyPairCredentialTest.php @@ -3,9 +3,9 @@ namespace AlibabaCloud\Credentials\Tests\Unit; use AlibabaCloud\Credentials\Credentials; -use AlibabaCloud\Credentials\Helper; +use AlibabaCloud\Credentials\Request\Request as Requests; +use AlibabaCloud\Credentials\Utils\Helper; use AlibabaCloud\Credentials\RsaKeyPairCredential; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use AlibabaCloud\Credentials\Tests\Unit\Ini\VirtualRsaKeyPairCredential; use Exception; use RuntimeException; @@ -83,7 +83,6 @@ public function testConstruct() (string)$credential ); $this->assertEquals([], $credential->getConfig()); - $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals($publicKeyId, $credential->getOriginalAccessKeyId()); } @@ -103,10 +102,10 @@ public function testSts() "SessionAccessKeySecret": "**************" } }'; - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); - Credentials::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); // Test $credential = new RsaKeyPairCredential($publicKeyId, $privateKeyFile); @@ -128,7 +127,7 @@ public function testStsIncomplete() // Setup $publicKeyId = 'public_key_id_new'; $privateKeyFile = VirtualRsaKeyPairCredential::privateKeyFileUrl(); - Credentials::cancelMock(); + Requests::cancelMock(); $result = '{ "RequestId": "F702286E-F231-4F40-BB86-XXXXXX", "SessionAccessKey": { @@ -136,7 +135,7 @@ public function testStsIncomplete() "Expiration": "2023-02-19T07:02:36.225Z" } }'; - Credentials::mockResponse(200, [], $result); + Requests::mockResponse(200, [], $result); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Result contains no credentials'); @@ -217,7 +216,7 @@ public function testPrivateKeyFileFormat() protected function initialize() { // Setup - Credentials::cancelMock(); + Requests::cancelMock(); // Setup $publicKeyId = 'public_key_id'; diff --git a/tests/Unit/StsCredentialTest.php b/tests/Unit/StsCredentialTest.php index 49e9a8d..483706b 100644 --- a/tests/Unit/StsCredentialTest.php +++ b/tests/Unit/StsCredentialTest.php @@ -2,7 +2,6 @@ namespace AlibabaCloud\Credentials\Tests\Unit; -use AlibabaCloud\Credentials\Signature\ShaHmac1Signature; use AlibabaCloud\Credentials\StsCredential; use PHPUnit\Framework\TestCase; use InvalidArgumentException; @@ -25,7 +24,6 @@ public function testConstruct() $this->assertEquals($accessKeySecret, $credential->getAccessKeySecret()); $this->assertEquals($securityToken, $credential->getSecurityToken()); $this->assertEquals($expiration, $credential->getExpiration()); - $this->assertInstanceOf(ShaHmac1Signature::class, $credential->getSignature()); $this->assertEquals( "$accessKeyId#$accessKeySecret#$securityToken", (string)$credential diff --git a/tests/Unit/Utils/FilterTest.php b/tests/Unit/Utils/FilterTest.php new file mode 100644 index 0000000..feef47a --- /dev/null +++ b/tests/Unit/Utils/FilterTest.php @@ -0,0 +1,332 @@ +getMessage()); + } + } + + /** + * @return array + */ + private 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 + */ + private 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 82% rename from tests/Unit/HelperTest.php rename to tests/Unit/Utils/HelperTest.php index 3189036..0df6587 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; @@ -115,10 +116,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 +142,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 +157,25 @@ 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()); + } } diff --git a/tests/Unit/MockTraitTest.php b/tests/Unit/Utils/MockTraitTest.php similarity index 100% rename from tests/Unit/MockTraitTest.php rename to tests/Unit/Utils/MockTraitTest.php