├── .github ├── ISSUE_TEMPLATE │ ├── bug-report-cn.md │ ├── bug-report.md │ ├── feature-request-cn.md │ ├── feature-request.md │ ├── help-cn.md │ └── help.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── go.yml ├── .gitignore ├── .scrutinizer.yml ├── CONTRIBUTING.md ├── LICENSE ├── README-CN.md ├── README.md ├── credentials ├── bearer_token_credential.go ├── bearer_token_credential_test.go ├── credential.go ├── credential_model.go ├── credential_model_test.go ├── credential_test.go ├── credential_updater.go ├── credential_updater_test.go ├── doc.go ├── ecs_ram_role_credentials_provider.go ├── ecs_ram_role_credentials_provider_test.go ├── env_provider.go ├── env_provider_test.go ├── instance_provider.go ├── instance_provider_test.go ├── internal │ ├── http │ │ ├── http.go │ │ └── http_test.go │ └── utils │ │ ├── path.go │ │ ├── path_test.go │ │ ├── runtime.go │ │ ├── runtime_test.go │ │ ├── utils.go │ │ └── utils_test.go ├── oidc_credential_provider.go ├── oidc_credential_provider_test.go ├── profile_provider.go ├── profile_provider_test.go ├── provider.go ├── provider_chain.go ├── provider_chain_test.go ├── providers │ ├── cli_profile.go │ ├── cli_profile_test.go │ ├── credentials.go │ ├── default.go │ ├── default_test.go │ ├── ecs_ram_role.go │ ├── ecs_ram_role_test.go │ ├── env.go │ ├── env_test.go │ ├── fixtures │ │ ├── .alibabacloud │ │ │ └── credentials │ │ ├── .aliyun │ │ │ └── config.json │ │ ├── invalid_cli_config.json │ │ ├── mock_cli_config.json │ │ ├── mock_empty_cli_config.json │ │ └── mock_oidctoken │ ├── hook.go │ ├── oidc.go │ ├── oidc_test.go │ ├── profile.go │ ├── profile_test.go │ ├── ram_role_arn.go │ ├── ram_role_arn_test.go │ ├── static_ak.go │ ├── static_ak_test.go │ ├── static_sts.go │ ├── static_sts_test.go │ ├── uri.go │ └── uri_test.go ├── ram_role_arn_credentials_provider.go ├── ram_role_arn_credentials_provider_test.go ├── request │ ├── common_request.go │ ├── common_request_test.go │ └── doc.go ├── response │ ├── common_response.go │ ├── common_response_test.go │ └── doc.go ├── rsa_key_pair_credentials_provider.go ├── rsa_key_pair_credentials_provider_test.go ├── session_credential.go ├── session_credential_test.go ├── uri_credential.go ├── uri_credential_test.go └── utils │ ├── doc.go │ ├── runtime.go │ ├── runtime_test.go │ ├── utils.go │ └── utils_test.go ├── doc.go ├── go.mod ├── go.sum ├── integration ├── auth_test.go └── proxy │ └── proxy_test.go └── test_fixtures ├── empty_oidc_token ├── oidc_token └── pk.pem /.github/ISSUE_TEMPLATE/bug-report-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B 缺陷问题反馈" 3 | about: 提交缺陷问题反馈 4 | 5 | --- 6 | 7 | 19 | 20 | * **Go 版本**: 21 | * **平台**: 22 | * **凭证类型**: 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | 20 | 21 | * **Go Version**: 22 | * **Platform**: 23 | * **Credential Type**: 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 功能建议" 3 | about: 建议在项目中增加功能 4 | 5 | --- 6 | 7 | 12 | 13 | **您的功能请求是否与问题有关? 请描述一下。** 14 | 请描述您要解决的问题。 15 | 16 | **描述你想要的解决方案** 17 | 请描述所需的行为。 18 | 19 | **描述您考虑过的替代方案** 20 | 请描述您考虑的替代解决方案或功能。 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | 12 | 13 | **Is your feature request related to a problem? Please describe.** 14 | Please describe the problem you are trying to solve. 15 | 16 | **Describe the solution you'd like** 17 | Please describe the desired behavior. 18 | 19 | **Describe alternatives you've considered** 20 | Please describe alternative solutions or features you have considered. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⁉️ 需要阿里云的帮助?" 3 | about: 请在我们的工单系统提出工单 4 | 5 | --- 6 | 7 | 如果您对阿里云 Credentials 的问题不是 Bug 或希望添加新功能, 8 | 请在我们的工单系统提出工单:https://selfservice.console.aliyun.com/ticket/createIndex 9 | 10 | 此类问题将被关闭。 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⁉️ Need help with Alibaba Cloud?" 3 | about: Please submit a work order in our work order system 4 | 5 | --- 6 | 7 | If you have a question about Alibaba Cloud that is not a bug report or feature 8 | request, please post it in https://selfservice.console.aliyun.com/ticket/createIndex 9 | 10 | Questions posted to this repository will be closed. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ##### You need to complete 11 | 12 | 13 | - [ ] unit tests and/or feature tests 14 | - [ ] documentation is changed or added -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | permissions: 10 | id-token: write 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | go: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20', '1.21', '1.22'] 19 | fail-fast: false 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Setup Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version: ${{ matrix.go }} 27 | 28 | - name: Go Mod 29 | run: go mod tidy 30 | 31 | - name: Test 32 | run: go test -race -coverprofile=coverage.txt -covermode=atomic ./credentials/... 33 | 34 | - name: Upload coverage infomartion 35 | uses: codecov/codecov-action@v4 36 | with: 37 | token: ${{ secrets.CODECOV_TOKEN }} 38 | 39 | - name: Setup OIDC 40 | run: npm install @actions/core@1.6.0 @actions/http-client 41 | 42 | - name: Get Id Token 43 | uses: actions/github-script@v7 44 | id: idtoken 45 | with: 46 | script: | 47 | const coreDemo = require('@actions/core'); 48 | const idToken = await coreDemo.getIDToken('sts.aliyuncs.com'); 49 | const fsx = require('fs/promises'); 50 | await fsx.writeFile('/tmp/oidc_token', idToken); 51 | 52 | - name: Integration Test 53 | run: go test -v -timeout 120s ./integration/... 54 | if: env.SUB_ALICLOUD_ACCESS_KEY != '' 55 | env: 56 | # for RAM role ARN 57 | SUB_ALICLOUD_ACCESS_KEY: ${{ secrets.SUB_ALICLOUD_ACCESS_KEY }} 58 | SUB_ALICLOUD_SECRET_KEY: ${{ secrets.SUB_ALICLOUD_SECRET_KEY }} 59 | ALICLOUD_ROLE_ARN: ${{ secrets.ALICLOUD_ROLE_ARN }} 60 | ALICLOUD_ROLE_SESSION_NAME: ${{ secrets.ALICLOUD_ROLE_SESSION_NAME }} 61 | ALICLOUD_ROLE_SESSION_EXPIRATION: ${{ secrets.ALICLOUD_ROLE_SESSION_EXPIRATION }} 62 | # for OIDC 63 | ALIBABA_CLOUD_OIDC_PROVIDER_ARN: ${{ secrets.OIDC_PROVIDER_ARN }} 64 | ALIBABA_CLOUD_OIDC_TOKEN_FILE: "/tmp/oidc_token" 65 | ALIBABA_CLOUD_ROLE_ARN: ${{ secrets.OIDC_ROLE_ARN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage/ 3 | coverage.txt -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | nodes: 3 | analysis: 4 | tests: 5 | override: 6 | - go-scrutinizer-run 7 | filter: 8 | excluded_paths: 9 | - integration/ 10 | dependency_paths: 11 | - vendor/ 12 | tools: 13 | external_code_coverage: true 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Alibaba Cloud Credentials for Go 2 | 3 | We work hard to provide a high-quality and useful Credential for Alibaba Cloud, and we greatly value feedback and contributions from our community. Please submit your [issues][issues] or [pull requests][pull-requests] through GitHub. 4 | 5 | ## Tips 6 | 7 | - The Credential is released under the [Apache license][license]. Any code you submit will be released under that license. For substantial contributions, we may ask you to sign a [Alibaba Documentation Corporate Contributor License Agreement (CLA)][cla]. 8 | - We maintain a high percentage of code coverage in our unit tests. If you make changes to the code, please add, update, and/or remove tests as appropriate. 9 | - If your code does not conform to the standards, does not include adequate tests, we may ask you to update your pull requests before we accept them. We also reserve the right to deny any pull requests that do not align with our standards or goals. 10 | 11 | [issues]: https://github.com/aliyun/credentials-go/issues 12 | [pull-requests]: https://github.com/aliyun/credentials-go/pulls 13 | [license]: http://www.apache.org/licenses/LICENSE-2.0 14 | [cla]: https://alibaba-cla-2018.oss-cn-beijing.aliyuncs.com/Alibaba_Documentation_Open_Source_Corporate_CLA.pdf 15 | -------------------------------------------------------------------------------- /credentials/bearer_token_credential.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import "github.com/alibabacloud-go/tea/tea" 4 | 5 | // BearerTokenCredential is a kind of credential 6 | type BearerTokenCredential struct { 7 | BearerToken string 8 | } 9 | 10 | // newBearerTokenCredential return a BearerTokenCredential object 11 | func newBearerTokenCredential(token string) *BearerTokenCredential { 12 | return &BearerTokenCredential{ 13 | BearerToken: token, 14 | } 15 | } 16 | 17 | func (s *BearerTokenCredential) GetCredential() (*CredentialModel, error) { 18 | credential := &CredentialModel{ 19 | BearerToken: tea.String(s.BearerToken), 20 | Type: tea.String("bearer"), 21 | ProviderName: tea.String("bearer"), 22 | } 23 | return credential, nil 24 | } 25 | 26 | // GetAccessKeyId is useless for BearerTokenCredential 27 | func (b *BearerTokenCredential) GetAccessKeyId() (*string, error) { 28 | return tea.String(""), nil 29 | } 30 | 31 | // GetAccessSecret is useless for BearerTokenCredential 32 | func (b *BearerTokenCredential) GetAccessKeySecret() (*string, error) { 33 | return tea.String(("")), nil 34 | } 35 | 36 | // GetSecurityToken is useless for BearerTokenCredential 37 | func (b *BearerTokenCredential) GetSecurityToken() (*string, error) { 38 | return tea.String(""), nil 39 | } 40 | 41 | // GetBearerToken reutrns BearerTokenCredential's BearerToken 42 | func (b *BearerTokenCredential) GetBearerToken() *string { 43 | return tea.String(b.BearerToken) 44 | } 45 | 46 | // GetType reutrns BearerTokenCredential's type 47 | func (b *BearerTokenCredential) GetType() *string { 48 | return tea.String("bearer") 49 | } 50 | -------------------------------------------------------------------------------- /credentials/bearer_token_credential_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_BearerTokenCredential(t *testing.T) { 10 | auth := newBearerTokenCredential("bearertoken") 11 | accessKeyId, err := auth.GetAccessKeyId() 12 | assert.Nil(t, err) 13 | assert.Equal(t, "", *accessKeyId) 14 | 15 | accessKeySecret, err := auth.GetAccessKeySecret() 16 | assert.Nil(t, err) 17 | assert.Equal(t, "", *accessKeySecret) 18 | 19 | token, err := auth.GetSecurityToken() 20 | assert.Nil(t, err) 21 | assert.Equal(t, "", *token) 22 | 23 | assert.Equal(t, "bearertoken", *auth.GetBearerToken()) 24 | assert.Equal(t, "bearer", *auth.GetType()) 25 | 26 | cred, err := auth.GetCredential() 27 | assert.Nil(t, err) 28 | assert.Equal(t, "bearertoken", *cred.BearerToken) 29 | assert.Nil(t, cred.AccessKeyId) 30 | assert.Nil(t, cred.AccessKeySecret) 31 | assert.Nil(t, cred.SecurityToken) 32 | assert.Equal(t, "bearer", *cred.Type) 33 | assert.Equal(t, "bearer", *cred.ProviderName) 34 | } 35 | -------------------------------------------------------------------------------- /credentials/credential_model.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import "github.com/alibabacloud-go/tea/tea" 4 | 5 | // CredentialModel is a model 6 | type CredentialModel struct { 7 | // accesskey id 8 | AccessKeyId *string `json:"accessKeyId,omitempty" xml:"accessKeyId,omitempty"` 9 | // accesskey secret 10 | AccessKeySecret *string `json:"accessKeySecret,omitempty" xml:"accessKeySecret,omitempty"` 11 | // security token 12 | SecurityToken *string `json:"securityToken,omitempty" xml:"securityToken,omitempty"` 13 | // bearer token 14 | BearerToken *string `json:"bearerToken,omitempty" xml:"bearerToken,omitempty"` 15 | // type 16 | // 17 | // example: 18 | // 19 | // access_key 20 | Type *string `json:"type,omitempty" xml:"type,omitempty"` 21 | // provider name 22 | // 23 | // example: 24 | // 25 | // cli_profile/static_ak 26 | ProviderName *string `json:"providerName,omitempty" xml:"providerName,omitempty"` 27 | } 28 | 29 | func (s CredentialModel) String() string { 30 | return tea.Prettify(s) 31 | } 32 | 33 | func (s CredentialModel) GoString() string { 34 | return s.String() 35 | } 36 | 37 | func (s *CredentialModel) SetAccessKeyId(v string) *CredentialModel { 38 | s.AccessKeyId = &v 39 | return s 40 | } 41 | 42 | func (s *CredentialModel) SetAccessKeySecret(v string) *CredentialModel { 43 | s.AccessKeySecret = &v 44 | return s 45 | } 46 | 47 | func (s *CredentialModel) SetSecurityToken(v string) *CredentialModel { 48 | s.SecurityToken = &v 49 | return s 50 | } 51 | 52 | func (s *CredentialModel) SetBearerToken(v string) *CredentialModel { 53 | s.BearerToken = &v 54 | return s 55 | } 56 | 57 | func (s *CredentialModel) SetType(v string) *CredentialModel { 58 | s.Type = &v 59 | return s 60 | } 61 | 62 | func (s *CredentialModel) SetProviderName(v string) *CredentialModel { 63 | s.ProviderName = &v 64 | return s 65 | } 66 | -------------------------------------------------------------------------------- /credentials/credential_model_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/alibabacloud-go/tea/tea" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_Credential(t *testing.T) { 11 | cred := &CredentialModel{ 12 | AccessKeyId: tea.String("AccessKeyId"), 13 | AccessKeySecret: tea.String("AccessKeySecret"), 14 | SecurityToken: tea.String("SecurityToken"), 15 | BearerToken: tea.String("BearerToken"), 16 | Type: tea.String("Type"), 17 | ProviderName: tea.String("ProviderName"), 18 | } 19 | assert.Equal(t, "AccessKeyId", *cred.AccessKeyId) 20 | assert.Equal(t, "AccessKeySecret", *cred.AccessKeySecret) 21 | assert.Equal(t, "SecurityToken", *cred.SecurityToken) 22 | assert.Equal(t, "BearerToken", *cred.BearerToken) 23 | assert.Equal(t, "Type", *cred.Type) 24 | assert.Equal(t, "ProviderName", *cred.ProviderName) 25 | 26 | assert.Equal(t, "{\n \"accessKeyId\": \"AccessKeyId\",\n \"accessKeySecret\": \"AccessKeySecret\",\n \"securityToken\": \"SecurityToken\",\n \"bearerToken\": \"BearerToken\",\n \"type\": \"Type\",\n \"providerName\": \"ProviderName\"\n}", cred.String()) 27 | assert.Equal(t, "{\n \"accessKeyId\": \"AccessKeyId\",\n \"accessKeySecret\": \"AccessKeySecret\",\n \"securityToken\": \"SecurityToken\",\n \"bearerToken\": \"BearerToken\",\n \"type\": \"Type\",\n \"providerName\": \"ProviderName\"\n}", cred.GoString()) 28 | 29 | cred = &CredentialModel{} 30 | cred.SetAccessKeyId("") 31 | cred.SetAccessKeySecret("") 32 | cred.SetSecurityToken("") 33 | assert.Equal(t, "", *cred.AccessKeyId) 34 | assert.Equal(t, "", *cred.AccessKeySecret) 35 | assert.Equal(t, "", *cred.SecurityToken) 36 | assert.Nil(t, cred.BearerToken) 37 | assert.Nil(t, cred.Type) 38 | assert.Nil(t, cred.ProviderName) 39 | } 40 | 41 | func Test_Credential2(t *testing.T) { 42 | cred := &CredentialModel{} 43 | cred.SetBearerToken("bearertoken") 44 | assert.Equal(t, "bearertoken", *cred.BearerToken) 45 | cred.SetType("bearertoken") 46 | cred.SetProviderName("bearertoken") 47 | assert.Equal(t, "bearertoken", *cred.Type) 48 | assert.Equal(t, "bearertoken", *cred.ProviderName) 49 | } 50 | -------------------------------------------------------------------------------- /credentials/credential_updater.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | ) 7 | 8 | const defaultInAdvanceScale = 0.95 9 | 10 | var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 11 | return fn 12 | } 13 | 14 | type credentialUpdater struct { 15 | credentialExpiration int 16 | lastUpdateTimestamp int64 17 | inAdvanceScale float64 18 | } 19 | 20 | func (updater *credentialUpdater) needUpdateCredential() (result bool) { 21 | if updater.inAdvanceScale == 0 { 22 | updater.inAdvanceScale = defaultInAdvanceScale 23 | } 24 | return time.Now().Unix()-updater.lastUpdateTimestamp >= int64(float64(updater.credentialExpiration)*updater.inAdvanceScale) 25 | } 26 | -------------------------------------------------------------------------------- /credentials/credential_updater_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_needUpdateCredential(t *testing.T) { 12 | updater := &credentialUpdater{ 13 | lastUpdateTimestamp: 100, 14 | credentialExpiration: 200, 15 | } 16 | isNeed := updater.needUpdateCredential() 17 | assert.True(t, isNeed) 18 | } 19 | 20 | func Test_hookdo(t *testing.T) { 21 | fn := func(req *http.Request) (*http.Response, error) { 22 | return nil, errors.New("hookdo") 23 | } 24 | result := hookDo(fn) 25 | resp, err := result(nil) 26 | assert.Nil(t, resp) 27 | assert.Equal(t, "hookdo", err.Error()) 28 | } 29 | -------------------------------------------------------------------------------- /credentials/doc.go: -------------------------------------------------------------------------------- 1 | // Package credentials is an alibaba cloud official credentials provider implementation 2 | package credentials 3 | -------------------------------------------------------------------------------- /credentials/ecs_ram_role_credentials_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/alibabacloud-go/tea/tea" 10 | "github.com/aliyun/credentials-go/credentials/internal/utils" 11 | "github.com/aliyun/credentials-go/credentials/request" 12 | ) 13 | 14 | var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/" 15 | var securityCredTokenURL = "http://100.100.100.200/latest/api/token" 16 | 17 | const defaultMetadataTokenDuration = int(21600) 18 | 19 | // ECSRAMRoleCredentialsProvider is a kind of credentials provider 20 | type ECSRAMRoleCredentialsProvider struct { 21 | *credentialUpdater 22 | RoleName string 23 | EnableIMDSv2 bool 24 | MetadataTokenDuration int 25 | sessionCredential *sessionCredential 26 | runtime *utils.Runtime 27 | metadataToken string 28 | staleTime int64 29 | } 30 | 31 | type ecsRAMRoleResponse struct { 32 | Code string `json:"Code" xml:"Code"` 33 | AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"` 34 | AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"` 35 | SecurityToken string `json:"SecurityToken" xml:"SecurityToken"` 36 | Expiration string `json:"Expiration" xml:"Expiration"` 37 | } 38 | 39 | func newEcsRAMRoleCredentialWithEnableIMDSv2(roleName string, enableIMDSv2 bool, metadataTokenDuration int, inAdvanceScale float64, runtime *utils.Runtime) *ECSRAMRoleCredentialsProvider { 40 | credentialUpdater := new(credentialUpdater) 41 | if inAdvanceScale < 1 && inAdvanceScale > 0 { 42 | credentialUpdater.inAdvanceScale = inAdvanceScale 43 | } 44 | return &ECSRAMRoleCredentialsProvider{ 45 | RoleName: roleName, 46 | EnableIMDSv2: enableIMDSv2, 47 | MetadataTokenDuration: metadataTokenDuration, 48 | credentialUpdater: credentialUpdater, 49 | runtime: runtime, 50 | } 51 | } 52 | 53 | func (e *ECSRAMRoleCredentialsProvider) GetCredential() (credentials *CredentialModel, err error) { 54 | if e.sessionCredential == nil || e.needUpdateCredential() { 55 | err = e.updateCredential() 56 | if err != nil { 57 | if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) { 58 | // 虽然有错误,但是已有的 credentials 还有效 59 | } else { 60 | return 61 | } 62 | } 63 | } 64 | 65 | credentials = &CredentialModel{ 66 | AccessKeyId: tea.String(e.sessionCredential.AccessKeyId), 67 | AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret), 68 | SecurityToken: tea.String(e.sessionCredential.SecurityToken), 69 | Type: tea.String("ecs_ram_role"), 70 | } 71 | 72 | return 73 | } 74 | 75 | // GetAccessKeyId reutrns EcsRAMRoleCredential's AccessKeyId 76 | // if AccessKeyId is not exist or out of date, the function will update it. 77 | func (e *ECSRAMRoleCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) { 78 | c, err := e.GetCredential() 79 | if err != nil { 80 | return 81 | } 82 | 83 | accessKeyId = c.AccessKeyId 84 | return 85 | } 86 | 87 | // GetAccessSecret reutrns EcsRAMRoleCredential's AccessKeySecret 88 | // if AccessKeySecret is not exist or out of date, the function will update it. 89 | func (e *ECSRAMRoleCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) { 90 | c, err := e.GetCredential() 91 | if err != nil { 92 | return 93 | } 94 | 95 | accessKeySecret = c.AccessKeySecret 96 | return 97 | } 98 | 99 | // GetSecurityToken reutrns EcsRAMRoleCredential's SecurityToken 100 | // if SecurityToken is not exist or out of date, the function will update it. 101 | func (e *ECSRAMRoleCredentialsProvider) GetSecurityToken() (securityToken *string, err error) { 102 | c, err := e.GetCredential() 103 | if err != nil { 104 | return 105 | } 106 | 107 | securityToken = c.SecurityToken 108 | return 109 | } 110 | 111 | // GetBearerToken is useless for EcsRAMRoleCredential 112 | func (e *ECSRAMRoleCredentialsProvider) GetBearerToken() *string { 113 | return tea.String("") 114 | } 115 | 116 | // GetType reutrns EcsRAMRoleCredential's type 117 | func (e *ECSRAMRoleCredentialsProvider) GetType() *string { 118 | return tea.String("ecs_ram_role") 119 | } 120 | 121 | func getRoleName() (string, error) { 122 | runtime := utils.NewRuntime(1, 1, "", "") 123 | request := request.NewCommonRequest() 124 | request.URL = securityCredURL 125 | request.Method = "GET" 126 | content, err := doAction(request, runtime) 127 | if err != nil { 128 | return "", err 129 | } 130 | return string(content), nil 131 | } 132 | 133 | func (e *ECSRAMRoleCredentialsProvider) getMetadataToken() (err error) { 134 | if e.needToRefresh() { 135 | if e.MetadataTokenDuration <= 0 { 136 | e.MetadataTokenDuration = defaultMetadataTokenDuration 137 | } 138 | tmpTime := time.Now().Unix() + int64(e.MetadataTokenDuration*1000) 139 | request := request.NewCommonRequest() 140 | request.URL = securityCredTokenURL 141 | request.Method = "PUT" 142 | request.Headers["X-aliyun-ecs-metadata-token-ttl-seconds"] = strconv.Itoa(e.MetadataTokenDuration) 143 | content, err := doAction(request, e.runtime) 144 | if err != nil { 145 | return err 146 | } 147 | e.staleTime = tmpTime 148 | e.metadataToken = string(content) 149 | } 150 | return 151 | } 152 | 153 | func (e *ECSRAMRoleCredentialsProvider) updateCredential() (err error) { 154 | if e.runtime == nil { 155 | e.runtime = new(utils.Runtime) 156 | } 157 | request := request.NewCommonRequest() 158 | if e.RoleName == "" { 159 | e.RoleName, err = getRoleName() 160 | if err != nil { 161 | return fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) 162 | } 163 | } 164 | if e.EnableIMDSv2 { 165 | err = e.getMetadataToken() 166 | if err != nil { 167 | return fmt.Errorf("failed to get token from ECS Metadata Service: %s", err.Error()) 168 | } 169 | request.Headers["X-aliyun-ecs-metadata-token"] = e.metadataToken 170 | } 171 | request.URL = securityCredURL + e.RoleName 172 | request.Method = "GET" 173 | content, err := doAction(request, e.runtime) 174 | if err != nil { 175 | return fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) 176 | } 177 | var resp *ecsRAMRoleResponse 178 | err = json.Unmarshal(content, &resp) 179 | if err != nil { 180 | return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error()) 181 | } 182 | if resp.Code != "Success" { 183 | return fmt.Errorf("refresh Ecs sts token err: Code is not Success") 184 | } 185 | if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" { 186 | return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration) 187 | } 188 | 189 | expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration) 190 | e.lastUpdateTimestamp = time.Now().Unix() 191 | e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) 192 | e.sessionCredential = &sessionCredential{ 193 | AccessKeyId: resp.AccessKeyId, 194 | AccessKeySecret: resp.AccessKeySecret, 195 | SecurityToken: resp.SecurityToken, 196 | } 197 | 198 | return 199 | } 200 | 201 | func (e *ECSRAMRoleCredentialsProvider) needToRefresh() (needToRefresh bool) { 202 | needToRefresh = time.Now().Unix() >= e.staleTime 203 | return 204 | } 205 | -------------------------------------------------------------------------------- /credentials/env_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | ) 9 | 10 | type envProvider struct{} 11 | 12 | var providerEnv = new(envProvider) 13 | 14 | const ( 15 | // EnvVarAccessKeyId is a name of ALIBABA_CLOUD_ACCESS_KEY_Id 16 | EnvVarAccessKeyId = "ALIBABA_CLOUD_ACCESS_KEY_Id" 17 | EnvVarAccessKeyIdNew = "ALIBABA_CLOUD_ACCESS_KEY_ID" 18 | // EnvVarAccessKeySecret is a name of ALIBABA_CLOUD_ACCESS_KEY_SECRET 19 | EnvVarAccessKeySecret = "ALIBABA_CLOUD_ACCESS_KEY_SECRET" 20 | ) 21 | 22 | func newEnvProvider() Provider { 23 | return &envProvider{} 24 | } 25 | 26 | func (p *envProvider) resolve() (config *Config, err error) { 27 | accessKeyId, ok1 := os.LookupEnv(EnvVarAccessKeyIdNew) 28 | if !ok1 || accessKeyId == "" { 29 | accessKeyId, ok1 = os.LookupEnv(EnvVarAccessKeyId) 30 | } 31 | accessKeySecret, ok2 := os.LookupEnv(EnvVarAccessKeySecret) 32 | if !ok1 || !ok2 { 33 | return nil, nil 34 | } 35 | if accessKeyId == "" { 36 | return nil, errors.New(EnvVarAccessKeyIdNew + " or " + EnvVarAccessKeyId + " cannot be empty") 37 | } 38 | if accessKeySecret == "" { 39 | return nil, errors.New(EnvVarAccessKeySecret + " cannot be empty") 40 | } 41 | 42 | securityToken := os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN") 43 | 44 | if securityToken != "" { 45 | config = &Config{ 46 | Type: tea.String("sts"), 47 | AccessKeyId: tea.String(accessKeyId), 48 | AccessKeySecret: tea.String(accessKeySecret), 49 | SecurityToken: tea.String(securityToken), 50 | } 51 | return 52 | } 53 | 54 | config = &Config{ 55 | Type: tea.String("access_key"), 56 | AccessKeyId: tea.String(accessKeyId), 57 | AccessKeySecret: tea.String(accessKeySecret), 58 | } 59 | 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /credentials/env_provider_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEnvresolve(t *testing.T) { 12 | p := newEnvProvider() 13 | assert.Equal(t, &envProvider{}, p) 14 | originAccessKeyIdNew := os.Getenv(EnvVarAccessKeyIdNew) 15 | originAccessKeyId := os.Getenv(EnvVarAccessKeyId) 16 | originAccessKeySecret := os.Getenv(EnvVarAccessKeySecret) 17 | originSecurityToken := os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN") 18 | os.Setenv(EnvVarAccessKeyId, "") 19 | os.Setenv(EnvVarAccessKeyIdNew, "") 20 | os.Setenv(EnvVarAccessKeySecret, "") 21 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", "") 22 | defer func() { 23 | os.Setenv(EnvVarAccessKeyIdNew, originAccessKeyIdNew) 24 | os.Setenv(EnvVarAccessKeyId, originAccessKeyId) 25 | os.Setenv(EnvVarAccessKeySecret, originAccessKeySecret) 26 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", originSecurityToken) 27 | }() 28 | c, err := p.resolve() 29 | assert.Nil(t, c) 30 | assert.EqualError(t, err, "ALIBABA_CLOUD_ACCESS_KEY_ID or ALIBABA_CLOUD_ACCESS_KEY_Id cannot be empty") 31 | 32 | os.Setenv(EnvVarAccessKeyIdNew, "") 33 | os.Setenv(EnvVarAccessKeyId, "") 34 | c, err = p.resolve() 35 | assert.Nil(t, c) 36 | assert.EqualError(t, err, "ALIBABA_CLOUD_ACCESS_KEY_ID or ALIBABA_CLOUD_ACCESS_KEY_Id cannot be empty") 37 | 38 | os.Setenv(EnvVarAccessKeyIdNew, "") 39 | os.Setenv(EnvVarAccessKeyId, "AccessKeyId") 40 | c, err = p.resolve() 41 | assert.Nil(t, c) 42 | assert.EqualError(t, err, "ALIBABA_CLOUD_ACCESS_KEY_SECRET cannot be empty") 43 | os.Setenv(EnvVarAccessKeySecret, "AccessKeySecret") 44 | c, err = p.resolve() 45 | assert.Nil(t, err) 46 | assert.Equal(t, "access_key", tea.StringValue(c.Type)) 47 | assert.Equal(t, "AccessKeyId", tea.StringValue(c.AccessKeyId)) 48 | assert.Equal(t, "AccessKeySecret", tea.StringValue(c.AccessKeySecret)) 49 | 50 | os.Setenv(EnvVarAccessKeyId, "AccessKeyId") 51 | os.Setenv(EnvVarAccessKeyIdNew, "AccessKeyIdNew") 52 | os.Setenv(EnvVarAccessKeySecret, "AccessKeySecret") 53 | c, err = p.resolve() 54 | assert.Nil(t, err) 55 | assert.Equal(t, "access_key", tea.StringValue(c.Type)) 56 | assert.Equal(t, "AccessKeyIdNew", tea.StringValue(c.AccessKeyId)) 57 | assert.Equal(t, "AccessKeySecret", tea.StringValue(c.AccessKeySecret)) 58 | 59 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", "token") 60 | c, err = p.resolve() 61 | assert.Nil(t, err) 62 | assert.Equal(t, "sts", tea.StringValue(c.Type)) 63 | assert.Equal(t, "AccessKeyIdNew", tea.StringValue(c.AccessKeyId)) 64 | assert.Equal(t, "AccessKeySecret", tea.StringValue(c.AccessKeySecret)) 65 | assert.Equal(t, "token", tea.StringValue(c.SecurityToken)) 66 | } 67 | -------------------------------------------------------------------------------- /credentials/instance_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | ) 9 | 10 | type instanceCredentialsProvider struct{} 11 | 12 | var providerInstance = new(instanceCredentialsProvider) 13 | 14 | func newInstanceCredentialsProvider() Provider { 15 | return &instanceCredentialsProvider{} 16 | } 17 | 18 | func (p *instanceCredentialsProvider) resolve() (*Config, error) { 19 | roleName, ok := os.LookupEnv(ENVEcsMetadata) 20 | if !ok { 21 | return nil, nil 22 | } 23 | enableIMDSv2, _ := os.LookupEnv(ENVEcsMetadataIMDSv2Enable) 24 | 25 | config := &Config{ 26 | Type: tea.String("ecs_ram_role"), 27 | RoleName: tea.String(roleName), 28 | EnableIMDSv2: tea.Bool(strings.ToLower(enableIMDSv2) == "true"), 29 | } 30 | return config, nil 31 | } 32 | -------------------------------------------------------------------------------- /credentials/instance_provider_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestInstanceCredentialsProvider(t *testing.T) { 12 | p := newInstanceCredentialsProvider() 13 | originEcsMetadata := os.Getenv(ENVEcsMetadata) 14 | os.Setenv(ENVEcsMetadata, "") 15 | defer func() { 16 | os.Setenv(ENVEcsMetadata, originEcsMetadata) 17 | }() 18 | c, err := p.resolve() 19 | assert.NotNil(t, c) 20 | assert.Nil(t, err) 21 | 22 | os.Setenv(ENVEcsMetadata, "role_name") 23 | c, err = p.resolve() 24 | assert.Nil(t, err) 25 | assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) 26 | assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) 27 | 28 | os.Setenv(ENVEcsMetadataIMDSv2Enable, "1") 29 | c, err = p.resolve() 30 | assert.Nil(t, err) 31 | assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) 32 | assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) 33 | 34 | os.Setenv(ENVEcsMetadataIMDSv2Enable, "1") 35 | c, err = p.resolve() 36 | assert.Nil(t, err) 37 | assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) 38 | assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) 39 | assert.False(t, tea.BoolValue(c.EnableIMDSv2)) 40 | 41 | os.Setenv(ENVEcsMetadataIMDSv2Enable, "false") 42 | c, err = p.resolve() 43 | assert.Nil(t, err) 44 | assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) 45 | assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) 46 | assert.False(t, tea.BoolValue(c.EnableIMDSv2)) 47 | 48 | os.Setenv(ENVEcsMetadataIMDSv2Enable, "true") 49 | c, err = p.resolve() 50 | assert.Nil(t, err) 51 | assert.Equal(t, "role_name", tea.StringValue(c.RoleName)) 52 | assert.Equal(t, "ecs_ram_role", tea.StringValue(c.Type)) 53 | assert.True(t, tea.BoolValue(c.EnableIMDSv2)) 54 | 55 | os.Unsetenv(ENVEcsMetadata) 56 | c, err = p.resolve() 57 | assert.Nil(t, c) 58 | assert.Nil(t, err) 59 | } 60 | -------------------------------------------------------------------------------- /credentials/internal/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | "net/url" 11 | "strings" 12 | "time" 13 | 14 | "github.com/alibabacloud-go/debug/debug" 15 | "github.com/aliyun/credentials-go/credentials/internal/utils" 16 | ) 17 | 18 | type Request struct { 19 | Method string // http request method 20 | URL string // http url 21 | Protocol string // http or https 22 | Host string // http host 23 | ReadTimeout time.Duration 24 | ConnectTimeout time.Duration 25 | Proxy string // http proxy 26 | Form map[string]string // http form 27 | Body []byte // request body for JSON or stream 28 | Path string 29 | Queries map[string]string 30 | Headers map[string]string 31 | } 32 | 33 | func (req *Request) BuildRequestURL() string { 34 | httpUrl := fmt.Sprintf("%s://%s%s", req.Protocol, req.Host, req.Path) 35 | if req.URL != "" { 36 | httpUrl = req.URL 37 | } 38 | 39 | querystring := utils.GetURLFormedMap(req.Queries) 40 | if querystring != "" { 41 | httpUrl = httpUrl + "?" + querystring 42 | } 43 | 44 | return fmt.Sprintf("%s %s", req.Method, httpUrl) 45 | } 46 | 47 | type Response struct { 48 | StatusCode int 49 | Headers map[string]string 50 | Body []byte 51 | } 52 | 53 | var newRequest = http.NewRequest 54 | 55 | type do func(req *http.Request) (*http.Response, error) 56 | 57 | var hookDo = func(fn do) do { 58 | return fn 59 | } 60 | 61 | var debuglog = debug.Init("credential") 62 | 63 | func Do(req *Request) (res *Response, err error) { 64 | querystring := utils.GetURLFormedMap(req.Queries) 65 | // do request 66 | httpUrl := fmt.Sprintf("%s://%s%s?%s", req.Protocol, req.Host, req.Path, querystring) 67 | if req.URL != "" { 68 | httpUrl = req.URL 69 | } 70 | 71 | var body io.Reader 72 | if req.Method == "GET" { 73 | body = strings.NewReader("") 74 | } else { 75 | body = strings.NewReader(utils.GetURLFormedMap(req.Form)) 76 | } 77 | 78 | httpRequest, err := newRequest(req.Method, httpUrl, body) 79 | if err != nil { 80 | return 81 | } 82 | 83 | if req.Form != nil { 84 | httpRequest.Header["Content-Type"] = []string{"application/x-www-form-urlencoded"} 85 | } 86 | 87 | for key, value := range req.Headers { 88 | if value != "" { 89 | debuglog("> %s: %s", key, value) 90 | httpRequest.Header.Set(key, value) 91 | } 92 | } 93 | 94 | httpClient := &http.Client{} 95 | 96 | if req.ReadTimeout != 0 { 97 | httpClient.Timeout = req.ReadTimeout + req.ConnectTimeout 98 | } 99 | 100 | transport := http.DefaultTransport.(*http.Transport).Clone() 101 | if req.Proxy != "" { 102 | var proxy *url.URL 103 | proxy, err = url.Parse(req.Proxy) 104 | if err != nil { 105 | return 106 | } 107 | transport.Proxy = http.ProxyURL(proxy) 108 | } 109 | 110 | if req.ConnectTimeout != 0 { 111 | transport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) { 112 | return (&net.Dialer{ 113 | Timeout: req.ConnectTimeout, 114 | DualStack: true, 115 | }).DialContext(ctx, network, address) 116 | } 117 | } 118 | 119 | httpClient.Transport = transport 120 | 121 | httpResponse, err := hookDo(httpClient.Do)(httpRequest) 122 | if err != nil { 123 | return 124 | } 125 | 126 | defer httpResponse.Body.Close() 127 | 128 | responseBody, err := ioutil.ReadAll(httpResponse.Body) 129 | if err != nil { 130 | return 131 | } 132 | res = &Response{ 133 | StatusCode: httpResponse.StatusCode, 134 | Headers: make(map[string]string), 135 | Body: responseBody, 136 | } 137 | for key, v := range httpResponse.Header { 138 | res.Headers[key] = v[0] 139 | } 140 | 141 | return 142 | } 143 | -------------------------------------------------------------------------------- /credentials/internal/http/http_test.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "io/ioutil" 7 | "net/http" 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestRequest(t *testing.T) { 15 | req := &Request{ 16 | Method: "GET", 17 | Protocol: "http", 18 | Host: "www.aliyun.com", 19 | Path: "/", 20 | } 21 | assert.Equal(t, "GET http://www.aliyun.com/", req.BuildRequestURL()) 22 | 23 | req = &Request{ 24 | Method: "GET", 25 | URL: "http://www.aliyun.com", 26 | Path: "/", 27 | } 28 | assert.Equal(t, "GET http://www.aliyun.com", req.BuildRequestURL()) 29 | 30 | // With query 31 | req = &Request{ 32 | Method: "GET", 33 | Protocol: "http", 34 | Host: "www.aliyun.com", 35 | Path: "/", 36 | Queries: map[string]string{ 37 | "spm": "test", 38 | }, 39 | } 40 | assert.Equal(t, "GET http://www.aliyun.com/?spm=test", req.BuildRequestURL()) 41 | } 42 | 43 | func TestDoGet(t *testing.T) { 44 | req := &Request{ 45 | Method: "GET", 46 | Protocol: "http", 47 | Host: "www.aliyun.com", 48 | Path: "/", 49 | } 50 | res, err := Do(req) 51 | assert.Nil(t, err) 52 | assert.NotNil(t, res) 53 | assert.Equal(t, 200, res.StatusCode) 54 | assert.Equal(t, "text/html; charset=utf-8", res.Headers["Content-Type"]) 55 | 56 | req = &Request{ 57 | Method: "GET", 58 | URL: "http://www.aliyun.com", 59 | } 60 | res, err = Do(req) 61 | assert.Nil(t, err) 62 | assert.NotNil(t, res) 63 | assert.Equal(t, 200, res.StatusCode) 64 | assert.Equal(t, "text/html; charset=utf-8", res.Headers["Content-Type"]) 65 | } 66 | 67 | func TestDoPost(t *testing.T) { 68 | req := &Request{ 69 | Method: "POST", 70 | Protocol: "http", 71 | Host: "www.aliyun.com", 72 | Path: "/", 73 | Form: map[string]string{ 74 | "URL": "HI", 75 | }, 76 | Headers: map[string]string{ 77 | "Accept-Language": "zh", 78 | }, 79 | } 80 | res, err := Do(req) 81 | assert.Nil(t, err) 82 | assert.NotNil(t, res) 83 | assert.Equal(t, 200, res.StatusCode) 84 | assert.Equal(t, "text/html; charset=utf-8", res.Headers["Content-Type"]) 85 | } 86 | 87 | type errorReader struct { 88 | } 89 | 90 | func (r *errorReader) Read(p []byte) (n int, err error) { 91 | err = errors.New("read failed") 92 | return 93 | } 94 | 95 | func TestDoWithError(t *testing.T) { 96 | originNewRequest := newRequest 97 | defer func() { newRequest = originNewRequest }() 98 | 99 | // case 1: mock new http request failed 100 | newRequest = func(method, url string, body io.Reader) (*http.Request, error) { 101 | return nil, errors.New("new http request failed") 102 | } 103 | 104 | req := &Request{ 105 | Method: "POST", 106 | Protocol: "http", 107 | Host: "www.aliyun.com", 108 | Path: "/", 109 | Form: map[string]string{ 110 | "URL": "HI", 111 | }, 112 | Headers: map[string]string{ 113 | "Accept-Language": "zh", 114 | }, 115 | } 116 | _, err := Do(req) 117 | assert.EqualError(t, err, "new http request failed") 118 | 119 | // reset new request 120 | newRequest = originNewRequest 121 | 122 | // case 2: server error 123 | originDo := hookDo 124 | defer func() { hookDo = originDo }() 125 | hookDo = func(fn do) do { 126 | return func(req *http.Request) (res *http.Response, err error) { 127 | err = errors.New("mock server error") 128 | return 129 | } 130 | } 131 | _, err = Do(req) 132 | assert.EqualError(t, err, "mock server error") 133 | 134 | // case 4: mock read response error 135 | hookDo = func(fn do) do { 136 | return func(req *http.Request) (res *http.Response, err error) { 137 | res = &http.Response{ 138 | Proto: "HTTP/1.1", 139 | ProtoMajor: 1, 140 | ProtoMinor: 1, 141 | Header: map[string][]string{}, 142 | StatusCode: 200, 143 | Status: "200 " + http.StatusText(200), 144 | } 145 | res.Body = ioutil.NopCloser(&errorReader{}) 146 | return 147 | } 148 | } 149 | 150 | _, err = Do(req) 151 | assert.EqualError(t, err, "read failed") 152 | } 153 | 154 | func TestDoWithProxy(t *testing.T) { 155 | req := &Request{ 156 | Method: "POST", 157 | Protocol: "http", 158 | Host: "www.aliyun.com", 159 | Path: "/", 160 | Form: map[string]string{ 161 | "URL": "HI", 162 | }, 163 | Headers: map[string]string{ 164 | "Accept-Language": "zh", 165 | }, 166 | Proxy: "http://localhost:9999/", 167 | } 168 | _, err := Do(req) 169 | assert.Contains(t, err.Error(), "proxyconnect tcp: dial tcp") 170 | assert.Contains(t, err.Error(), "connect: connection refused") 171 | 172 | // invalid proxy url 173 | req.Proxy = string([]byte{0x7f}) 174 | _, err = Do(req) 175 | assert.Contains(t, err.Error(), "net/url: invalid control character in URL") 176 | } 177 | 178 | func TestDoWithConnectTimeout(t *testing.T) { 179 | req := &Request{ 180 | Method: "POST", 181 | Protocol: "http", 182 | Host: "www.aliyun.com", 183 | Path: "/", 184 | Form: map[string]string{ 185 | "URL": "HI", 186 | }, 187 | Headers: map[string]string{ 188 | "Accept-Language": "zh", 189 | }, 190 | ConnectTimeout: 1 * time.Nanosecond, 191 | } 192 | _, err := Do(req) 193 | assert.Contains(t, err.Error(), "dial tcp: ") 194 | assert.Contains(t, err.Error(), "i/o timeout") 195 | } 196 | 197 | func TestDoWithReadTimeout(t *testing.T) { 198 | req := &Request{ 199 | Method: "POST", 200 | Protocol: "http", 201 | Host: "www.aliyun.com", 202 | Path: "/", 203 | ReadTimeout: 1 * time.Nanosecond, 204 | } 205 | _, err := Do(req) 206 | assert.Contains(t, err.Error(), "(Client.Timeout exceeded while awaiting headers)") 207 | } 208 | -------------------------------------------------------------------------------- /credentials/internal/utils/path.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | ) 7 | 8 | var getOS = func() string { 9 | return runtime.GOOS 10 | } 11 | 12 | func GetHomePath() string { 13 | if getOS() == "windows" { 14 | return os.Getenv("USERPROFILE") 15 | } 16 | 17 | return os.Getenv("HOME") 18 | } 19 | -------------------------------------------------------------------------------- /credentials/internal/utils/path_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_getOS(t *testing.T) { 12 | assert.Equal(t, runtime.GOOS, getOS()) 13 | } 14 | 15 | func TestGetHomePath(t *testing.T) { 16 | originGetOS := getOS 17 | originUserProfile := os.Getenv("USERPROFILE") 18 | originHome := os.Getenv("HOME") 19 | defer func() { 20 | getOS = originGetOS 21 | os.Setenv("USERPROFILE", originUserProfile) 22 | os.Setenv("HOME", originHome) 23 | }() 24 | 25 | getOS = func() string { 26 | return "windows" 27 | } 28 | os.Setenv("USERPROFILE", "/path/to/custom_home") 29 | 30 | assert.Equal(t, "/path/to/custom_home", GetHomePath()) 31 | 32 | getOS = func() string { 33 | return "darwin" 34 | } 35 | 36 | os.Setenv("HOME", "/Users/jacksontian") 37 | assert.Equal(t, "/Users/jacksontian", GetHomePath()) 38 | } 39 | -------------------------------------------------------------------------------- /credentials/internal/utils/runtime.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // Runtime is for setting timeout, proxy and host 10 | type Runtime struct { 11 | ReadTimeout int 12 | ConnectTimeout int 13 | Proxy string 14 | Host string 15 | STSEndpoint string 16 | } 17 | 18 | // NewRuntime returns a Runtime 19 | func NewRuntime(readTimeout, connectTimeout int, proxy string, host string) *Runtime { 20 | return &Runtime{ 21 | ReadTimeout: readTimeout, 22 | ConnectTimeout: connectTimeout, 23 | Proxy: proxy, 24 | Host: host, 25 | } 26 | } 27 | 28 | // Timeout is for connect Timeout 29 | func Timeout(connectTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) { 30 | return func(ctx context.Context, network, address string) (net.Conn, error) { 31 | return (&net.Dialer{ 32 | Timeout: connectTimeout, 33 | DualStack: true, 34 | }).DialContext(ctx, network, address) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /credentials/internal/utils/runtime_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_NewRuntime(t *testing.T) { 12 | runitme := NewRuntime(10, 10, "proxy", "host") 13 | assert.Equal(t, 10, runitme.ReadTimeout) 14 | assert.Equal(t, 10, runitme.ConnectTimeout) 15 | assert.Equal(t, "proxy", runitme.Proxy) 16 | assert.Equal(t, "host", runitme.Host) 17 | 18 | dialContext := Timeout(5 * time.Second) 19 | ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second) 20 | assert.NotNil(t, cancelFunc) 21 | c, err := dialContext(ctx, "127.0.0.1", "127.0.0.2") 22 | assert.Nil(t, c) 23 | assert.Equal(t, "dial 127.0.0.1: unknown network 127.0.0.1", err.Error()) 24 | } 25 | -------------------------------------------------------------------------------- /credentials/internal/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/hmac" 7 | "crypto/md5" 8 | "crypto/rand" 9 | "crypto/rsa" 10 | "crypto/sha1" 11 | "crypto/x509" 12 | "encoding/base64" 13 | "encoding/hex" 14 | "fmt" 15 | "hash" 16 | "io" 17 | mathrand "math/rand" 18 | "net/url" 19 | "os" 20 | "runtime" 21 | "strconv" 22 | "sync/atomic" 23 | "time" 24 | ) 25 | 26 | type uuid [16]byte 27 | 28 | const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 29 | 30 | var hookRead = func(fn func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { 31 | return fn 32 | } 33 | 34 | var hookRSA = func(fn func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 35 | return fn 36 | } 37 | 38 | // GetUUID returns a uuid 39 | func GetUUID() (uuidHex string) { 40 | uuid := newUUID() 41 | uuidHex = hex.EncodeToString(uuid[:]) 42 | return 43 | } 44 | 45 | // RandStringBytes returns a rand string 46 | func RandStringBytes(n int) string { 47 | b := make([]byte, n) 48 | for i := range b { 49 | b[i] = letterBytes[mathrand.Intn(len(letterBytes))] 50 | } 51 | return string(b) 52 | } 53 | 54 | // ShaHmac1 return a string which has been hashed 55 | func ShaHmac1(source, secret string) string { 56 | key := []byte(secret) 57 | hmac := hmac.New(sha1.New, key) 58 | hmac.Write([]byte(source)) 59 | signedBytes := hmac.Sum(nil) 60 | signedString := base64.StdEncoding.EncodeToString(signedBytes) 61 | return signedString 62 | } 63 | 64 | // Sha256WithRsa return a string which has been hashed with Rsa 65 | func Sha256WithRsa(source, secret string) string { 66 | decodeString, err := base64.StdEncoding.DecodeString(secret) 67 | if err != nil { 68 | panic(err) 69 | } 70 | private, err := x509.ParsePKCS8PrivateKey(decodeString) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | h := crypto.Hash.New(crypto.SHA256) 76 | h.Write([]byte(source)) 77 | hashed := h.Sum(nil) 78 | signature, err := hookRSA(rsa.SignPKCS1v15)(rand.Reader, private.(*rsa.PrivateKey), 79 | crypto.SHA256, hashed) 80 | if err != nil { 81 | panic(err) 82 | } 83 | 84 | return base64.StdEncoding.EncodeToString(signature) 85 | } 86 | 87 | // GetMD5Base64 returns a string which has been base64 88 | func GetMD5Base64(bytes []byte) (base64Value string) { 89 | md5Ctx := md5.New() 90 | md5Ctx.Write(bytes) 91 | md5Value := md5Ctx.Sum(nil) 92 | base64Value = base64.StdEncoding.EncodeToString(md5Value) 93 | return 94 | } 95 | 96 | // GetTimeInFormatISO8601 returns a time string 97 | func GetTimeInFormatISO8601() (timeStr string) { 98 | gmt := time.FixedZone("GMT", 0) 99 | 100 | return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") 101 | } 102 | 103 | // GetURLFormedMap returns a url encoded string 104 | func GetURLFormedMap(source map[string]string) (urlEncoded string) { 105 | urlEncoder := url.Values{} 106 | for key, value := range source { 107 | urlEncoder.Add(key, value) 108 | } 109 | urlEncoded = urlEncoder.Encode() 110 | return 111 | } 112 | 113 | func newUUID() uuid { 114 | ns := uuid{} 115 | safeRandom(ns[:]) 116 | u := newFromHash(md5.New(), ns, RandStringBytes(16)) 117 | u[6] = (u[6] & 0x0f) | (byte(2) << 4) 118 | u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) 119 | 120 | return u 121 | } 122 | 123 | func newFromHash(h hash.Hash, ns uuid, name string) uuid { 124 | u := uuid{} 125 | h.Write(ns[:]) 126 | h.Write([]byte(name)) 127 | copy(u[:], h.Sum(nil)) 128 | 129 | return u 130 | } 131 | 132 | func safeRandom(dest []byte) { 133 | if _, err := hookRead(rand.Read)(dest); err != nil { 134 | panic(err) 135 | } 136 | } 137 | 138 | func (u uuid) String() string { 139 | buf := make([]byte, 36) 140 | 141 | hex.Encode(buf[0:8], u[0:4]) 142 | buf[8] = '-' 143 | hex.Encode(buf[9:13], u[4:6]) 144 | buf[13] = '-' 145 | hex.Encode(buf[14:18], u[6:8]) 146 | buf[18] = '-' 147 | hex.Encode(buf[19:23], u[8:10]) 148 | buf[23] = '-' 149 | hex.Encode(buf[24:], u[10:]) 150 | 151 | return string(buf) 152 | } 153 | 154 | var processStartTime int64 = time.Now().UnixNano() / 1e6 155 | var seqId int64 = 0 156 | 157 | func getGID() uint64 { 158 | // https://blog.sgmansfield.com/2015/12/goroutine-ids/ 159 | b := make([]byte, 64) 160 | b = b[:runtime.Stack(b, false)] 161 | b = bytes.TrimPrefix(b, []byte("goroutine ")) 162 | b = b[:bytes.IndexByte(b, ' ')] 163 | n, _ := strconv.ParseUint(string(b), 10, 64) 164 | return n 165 | } 166 | 167 | func GetNonce() (uuidHex string) { 168 | routineId := getGID() 169 | currentTime := time.Now().UnixNano() / 1e6 170 | seq := atomic.AddInt64(&seqId, 1) 171 | randNum := mathrand.Int63() 172 | msg := fmt.Sprintf("%d-%d-%d-%d-%d", processStartTime, routineId, currentTime, seq, randNum) 173 | h := md5.New() 174 | h.Write([]byte(msg)) 175 | return hex.EncodeToString(h.Sum(nil)) 176 | } 177 | 178 | // Get first non-empty value 179 | func GetDefaultString(values ...string) string { 180 | for _, v := range values { 181 | if v != "" { 182 | return v 183 | } 184 | } 185 | 186 | return "" 187 | } 188 | 189 | // set back the memoried enviroment variables 190 | type Rollback func() 191 | 192 | func Memory(keys ...string) Rollback { 193 | // remenber enviroment variables 194 | m := make(map[string]string) 195 | for _, key := range keys { 196 | m[key] = os.Getenv(key) 197 | } 198 | 199 | return func() { 200 | for _, key := range keys { 201 | os.Setenv(key, m[key]) 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /credentials/internal/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "errors" 7 | "io" 8 | "os" 9 | "regexp" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestGetUUID(t *testing.T) { 16 | uuid := newUUID() 17 | assert.Equal(t, 16, len(uuid)) 18 | assert.Equal(t, 36, len(uuid.String())) 19 | uuidString := GetUUID() 20 | assert.Equal(t, 32, len(uuidString)) 21 | } 22 | 23 | func TestGetMD5Base64(t *testing.T) { 24 | assert.Equal(t, "ERIHLmRX2uZmssDdxQnnxQ==", 25 | GetMD5Base64([]byte("That's all folks!!"))) 26 | assert.Equal(t, "GsJRdI3kAbAnHo/0+3wWJw==", 27 | GetMD5Base64([]byte("中文也没啥问题"))) 28 | } 29 | 30 | func TestGetTimeInFormatISO8601(t *testing.T) { 31 | s := GetTimeInFormatISO8601() 32 | assert.Equal(t, 20, len(s)) 33 | // 2006-01-02T15:04:05Z 34 | re := regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$`) 35 | assert.True(t, re.MatchString(s)) 36 | } 37 | 38 | func TestGetURLFormedMap(t *testing.T) { 39 | m := make(map[string]string) 40 | m["key"] = "value" 41 | s := GetURLFormedMap(m) 42 | assert.Equal(t, "key=value", s) 43 | m["key2"] = "http://domain/?key=value&key2=value2" 44 | s2 := GetURLFormedMap(m) 45 | assert.Equal(t, "key=value&key2=http%3A%2F%2Fdomain%2F%3Fkey%3Dvalue%26key2%3Dvalue2", s2) 46 | } 47 | 48 | func TestShaHmac1(t *testing.T) { 49 | result := ShaHmac1("source", "secret") 50 | assert.Equal(t, "Jv4yi8SobFhg5t1C7nWLbhBSFZQ=", result) 51 | 52 | assert.Equal(t, "CqCYIa39h9SSWuXnTz8F5hh9UPA=", ShaHmac1("中文", "secret")) 53 | } 54 | 55 | func TestSha256WithRsa(t *testing.T) { 56 | secret := ` 57 | MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa 58 | 3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 59 | 3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ 60 | FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA 61 | kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I 62 | oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk 63 | Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq 64 | 1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu 65 | WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb 66 | WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN 67 | Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 68 | bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm 69 | 2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc 70 | vftlY0Hs1vNXcaBgEA==` 71 | result := Sha256WithRsa("source", secret) 72 | assert.Equal(t, "UNyJPD27jjSNl70b02E/DUtgtNESdtAuxbNBZTlksk1t/GYjiQNRlFIubp/EGKcWsqs7p5SFKnNiSRqWG3A51VmJFBXXtyW1nwLC9xY/MbUj6JVWNYCuLkPWM942O+GAk7N+G8ZQZt7ib2MhruDAUmv1lLN26lDaCPBX2MJQJCo=", result) 73 | 74 | assert.Equal(t, "CKE0osxUnFFH+oYP3Q427saucBuignE+Mrni63G9w46yZFtVoXFOu5lNiNCnUtaPNpGmBf9X5oGCY+otqPf7bP93nB59rfdteQs0sS65PWH9yjH8RwYCWGCbuyRul/0qIv/nYYGzkLON1C1Vx9Z4Yep6llYuJang5RIXrAkQLmQ=", Sha256WithRsa("中文", secret)) 75 | } 76 | 77 | func TestSha256WithRsa_DecodeString_Error(t *testing.T) { 78 | defer func() { // 进行异常捕捉 79 | err := recover() 80 | assert.NotNil(t, err) 81 | assert.Equal(t, "illegal base64 data at input byte 0", err.(error).Error()) 82 | }() 83 | secret := `==` 84 | Sha256WithRsa("source", secret) 85 | } 86 | 87 | func TestSha256WithRsa_ParsePKCS8PrivateKey_Error(t *testing.T) { 88 | defer func() { // 进行异常捕捉 89 | err := recover() 90 | assert.NotNil(t, err) 91 | assert.Equal(t, "asn1: structure error: length too large", err.(error).Error()) 92 | }() 93 | secret := `Jv4yi8SobFhg5t1C7nWLbhBSFZQ=` 94 | Sha256WithRsa("source", secret) 95 | } 96 | 97 | func TestHookRead(t *testing.T) { 98 | fn := func(p []byte) (n int, err error) { 99 | return 0, errors.New("hookRead") 100 | } 101 | result := hookRead(fn) 102 | n, err := result(nil) 103 | assert.Equal(t, 0, n) 104 | assert.Equal(t, "hookRead", err.Error()) 105 | 106 | originHookRead := hookRead 107 | hookRead = func(old func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { 108 | return fn 109 | } 110 | defer func() { 111 | err := recover() 112 | assert.Equal(t, "hookRead", err.(error).Error()) 113 | hookRead = originHookRead 114 | }() 115 | safeRandom([]byte("credentialtest")) 116 | } 117 | 118 | func TestHookRSA(t *testing.T) { 119 | fn := func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 120 | return nil, errors.New("hookRSA") 121 | } 122 | result := hookRSA(fn) 123 | hash := crypto.Hash(10) 124 | byt, err := result(nil, nil, hash, nil) 125 | assert.Nil(t, byt) 126 | assert.Equal(t, "hookRSA", err.Error()) 127 | 128 | originHookRSA := hookRSA 129 | hookRSA = func(old func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 130 | return fn 131 | } 132 | defer func() { 133 | err := recover() 134 | assert.Equal(t, "hookRSA", err.(error).Error()) 135 | hookRSA = originHookRSA 136 | }() 137 | secret := ` 138 | MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa 139 | 3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 140 | 3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ 141 | FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA 142 | kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I 143 | oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk 144 | Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq 145 | 1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu 146 | WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb 147 | WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN 148 | Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 149 | bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm 150 | 2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc 151 | vftlY0Hs1vNXcaBgEA==` 152 | Sha256WithRsa("source", secret) 153 | } 154 | 155 | func TestGetDefaultString(t *testing.T) { 156 | assert.Equal(t, "default", GetDefaultString("", "default")) 157 | assert.Equal(t, "custom", GetDefaultString("custom", "default")) 158 | assert.Equal(t, "", GetDefaultString("", "", "")) 159 | } 160 | 161 | func TestMemoryAndRollback(t *testing.T) { 162 | os.Setenv("test", "old") 163 | rollback := Memory("test") 164 | os.Setenv("test", "new") 165 | rollback() 166 | 167 | assert.Equal(t, "old", os.Getenv("test")) 168 | } 169 | 170 | func TestGetNonce(t *testing.T) { 171 | assert.Equal(t, 32, len(GetNonce())) 172 | assert.NotEqual(t, GetNonce(), GetNonce()) 173 | } 174 | -------------------------------------------------------------------------------- /credentials/oidc_credential_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/alibabacloud-go/tea/tea" 7 | ) 8 | 9 | type oidcCredentialsProvider struct{} 10 | 11 | var providerOIDC = new(oidcCredentialsProvider) 12 | 13 | func newOidcCredentialsProvider() Provider { 14 | return &oidcCredentialsProvider{} 15 | } 16 | 17 | func (p *oidcCredentialsProvider) resolve() (*Config, error) { 18 | roleArn, ok1 := os.LookupEnv(ENVRoleArn) 19 | oidcProviderArn, ok2 := os.LookupEnv(ENVOIDCProviderArn) 20 | oidcTokenFilePath, ok3 := os.LookupEnv(ENVOIDCTokenFile) 21 | if !ok1 || !ok2 || !ok3 { 22 | return nil, nil 23 | } 24 | 25 | config := &Config{ 26 | Type: tea.String("oidc_role_arn"), 27 | RoleArn: tea.String(roleArn), 28 | OIDCProviderArn: tea.String(oidcProviderArn), 29 | OIDCTokenFilePath: tea.String(oidcTokenFilePath), 30 | RoleSessionName: tea.String("defaultSessionName"), 31 | } 32 | roleSessionName, ok := os.LookupEnv(ENVRoleSessionName) 33 | if ok { 34 | config.RoleSessionName = tea.String(roleSessionName) 35 | } 36 | return config, nil 37 | } 38 | -------------------------------------------------------------------------------- /credentials/oidc_credential_provider_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestOidcCredentialsProvider(t *testing.T) { 12 | p := newOidcCredentialsProvider() 13 | roleArn := os.Getenv(ENVRoleArn) 14 | oidcProviderArn := os.Getenv(ENVOIDCProviderArn) 15 | oidcTokenFilePath := os.Getenv(ENVOIDCTokenFile) 16 | roleSessionName := os.Getenv(ENVRoleSessionName) 17 | os.Setenv(ENVRoleArn, "") 18 | os.Setenv(ENVOIDCProviderArn, "") 19 | os.Setenv(ENVOIDCTokenFile, "") 20 | os.Setenv(ENVRoleSessionName, "") 21 | defer func() { 22 | os.Setenv(ENVRoleArn, roleArn) 23 | os.Setenv(ENVOIDCProviderArn, oidcProviderArn) 24 | os.Setenv(ENVOIDCTokenFile, oidcTokenFilePath) 25 | os.Setenv(ENVRoleSessionName, roleSessionName) 26 | }() 27 | c, err := p.resolve() 28 | assert.NotNil(t, c) 29 | assert.Nil(t, err) 30 | 31 | os.Setenv(ENVRoleArn, "roleArn") 32 | os.Setenv(ENVOIDCProviderArn, "oidcProviderArn") 33 | os.Setenv(ENVOIDCTokenFile, "oidcTokenFilePath") 34 | os.Unsetenv(ENVRoleSessionName) 35 | c, err = p.resolve() 36 | assert.Nil(t, err) 37 | assert.Equal(t, "roleArn", tea.StringValue(c.RoleArn)) 38 | assert.Equal(t, "oidcProviderArn", tea.StringValue(c.OIDCProviderArn)) 39 | assert.Equal(t, "oidcTokenFilePath", tea.StringValue(c.OIDCTokenFilePath)) 40 | assert.Equal(t, "defaultSessionName", tea.StringValue(c.RoleSessionName)) 41 | assert.Equal(t, "oidc_role_arn", tea.StringValue(c.Type)) 42 | 43 | os.Setenv(ENVRoleSessionName, "roleSessionName") 44 | c, err = p.resolve() 45 | assert.Nil(t, err) 46 | assert.Equal(t, "roleArn", tea.StringValue(c.RoleArn)) 47 | assert.Equal(t, "oidcProviderArn", tea.StringValue(c.OIDCProviderArn)) 48 | assert.Equal(t, "oidcTokenFilePath", tea.StringValue(c.OIDCTokenFilePath)) 49 | assert.Equal(t, "roleSessionName", tea.StringValue(c.RoleSessionName)) 50 | assert.Equal(t, "oidc_role_arn", tea.StringValue(c.Type)) 51 | 52 | os.Unsetenv(ENVRoleArn) 53 | os.Unsetenv(ENVOIDCProviderArn) 54 | os.Unsetenv(ENVOIDCTokenFile) 55 | os.Unsetenv(ENVRoleSessionName) 56 | c, err = p.resolve() 57 | assert.Nil(t, c) 58 | assert.Nil(t, err) 59 | } 60 | -------------------------------------------------------------------------------- /credentials/provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | // Environmental virables that may be used by the provider 4 | const ( 5 | ENVCredentialFile = "ALIBABA_CLOUD_CREDENTIALS_FILE" 6 | ENVEcsMetadata = "ALIBABA_CLOUD_ECS_METADATA" 7 | ENVEcsMetadataIMDSv2Enable = "ALIBABA_CLOUD_ECS_IMDSV2_ENABLE" 8 | PATHCredentialFile = "~/.alibabacloud/credentials" 9 | ENVRoleArn = "ALIBABA_CLOUD_ROLE_ARN" 10 | ENVOIDCProviderArn = "ALIBABA_CLOUD_OIDC_PROVIDER_ARN" 11 | ENVOIDCTokenFile = "ALIBABA_CLOUD_OIDC_TOKEN_FILE" 12 | ENVRoleSessionName = "ALIBABA_CLOUD_ROLE_SESSION_NAME" 13 | ) 14 | 15 | // Provider will be implemented When you want to customize the provider. 16 | type Provider interface { 17 | resolve() (*Config, error) 18 | } 19 | -------------------------------------------------------------------------------- /credentials/provider_chain.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type providerChain struct { 8 | Providers []Provider 9 | } 10 | 11 | var defaultproviders = []Provider{providerEnv, providerOIDC, providerProfile, providerInstance} 12 | var defaultChain = newProviderChain(defaultproviders) 13 | 14 | func newProviderChain(providers []Provider) Provider { 15 | return &providerChain{ 16 | Providers: providers, 17 | } 18 | } 19 | 20 | func (p *providerChain) resolve() (*Config, error) { 21 | for _, provider := range p.Providers { 22 | config, err := provider.resolve() 23 | if err != nil { 24 | return nil, err 25 | } else if config == nil { 26 | continue 27 | } 28 | return config, err 29 | } 30 | return nil, errors.New("no credential found") 31 | 32 | } 33 | -------------------------------------------------------------------------------- /credentials/provider_chain_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestProviderChain(t *testing.T) { 12 | env := newEnvProvider() 13 | pp := newProfileProvider() 14 | instanceP := newInstanceCredentialsProvider() 15 | 16 | pc := newProviderChain([]Provider{env, pp, instanceP}) 17 | 18 | originAccessKeyIdNew := os.Getenv(EnvVarAccessKeyIdNew) 19 | originAccessKeyId := os.Getenv(EnvVarAccessKeyId) 20 | originAccessKeySecret := os.Getenv(EnvVarAccessKeySecret) 21 | os.Setenv(EnvVarAccessKeyId, "") 22 | os.Setenv(EnvVarAccessKeyIdNew, "") 23 | os.Setenv(EnvVarAccessKeySecret, "") 24 | defer func() { 25 | os.Setenv(EnvVarAccessKeyIdNew, originAccessKeyIdNew) 26 | os.Setenv(EnvVarAccessKeyId, originAccessKeyId) 27 | os.Setenv(EnvVarAccessKeySecret, originAccessKeySecret) 28 | }() 29 | c, err := pc.resolve() 30 | assert.Nil(t, c) 31 | assert.EqualError(t, err, "ALIBABA_CLOUD_ACCESS_KEY_ID or ALIBABA_CLOUD_ACCESS_KEY_Id cannot be empty") 32 | 33 | os.Setenv(EnvVarAccessKeyId, "AccessKeyId") 34 | os.Setenv(EnvVarAccessKeySecret, "AccessKeySecret") 35 | c, err = pc.resolve() 36 | assert.NotNil(t, c) 37 | assert.Nil(t, err) 38 | 39 | os.Unsetenv(EnvVarAccessKeyId) 40 | os.Unsetenv(EnvVarAccessKeySecret) 41 | os.Unsetenv(ENVCredentialFile) 42 | os.Unsetenv(ENVEcsMetadata) 43 | 44 | c, err = pc.resolve() 45 | assert.Nil(t, c) 46 | assert.EqualError(t, err, "no credential found") 47 | } 48 | 49 | func TestDefaultChainNoCred(t *testing.T) { 50 | accessKeyIdNew := os.Getenv(EnvVarAccessKeyIdNew) 51 | accessKeyId := os.Getenv(EnvVarAccessKeyId) 52 | accessKeySecret := os.Getenv(EnvVarAccessKeySecret) 53 | ecsMetadata := os.Getenv(ENVEcsMetadata) 54 | roleArn := os.Getenv(ENVRoleArn) 55 | oidcProviderArn := os.Getenv(ENVOIDCProviderArn) 56 | oidcTokenFilePath := os.Getenv(ENVOIDCTokenFile) 57 | roleSessionName := os.Getenv(ENVRoleSessionName) 58 | os.Unsetenv(EnvVarAccessKeyId) 59 | os.Unsetenv(EnvVarAccessKeySecret) 60 | os.Unsetenv(ENVCredentialFile) 61 | os.Unsetenv(ENVEcsMetadata) 62 | os.Unsetenv(ENVRoleArn) 63 | os.Unsetenv(ENVOIDCProviderArn) 64 | os.Unsetenv(ENVOIDCTokenFile) 65 | os.Unsetenv(ENVRoleSessionName) 66 | defer func() { 67 | os.Setenv(EnvVarAccessKeyIdNew, accessKeyIdNew) 68 | os.Setenv(EnvVarAccessKeyId, accessKeyId) 69 | os.Setenv(EnvVarAccessKeySecret, accessKeySecret) 70 | os.Setenv(ENVEcsMetadata, ecsMetadata) 71 | os.Setenv(ENVRoleArn, roleArn) 72 | os.Setenv(ENVOIDCProviderArn, oidcProviderArn) 73 | os.Setenv(ENVOIDCTokenFile, oidcTokenFilePath) 74 | os.Setenv(ENVRoleSessionName, roleSessionName) 75 | }() 76 | 77 | chain, err := defaultChain.resolve() 78 | assert.Nil(t, chain) 79 | assert.Equal(t, "no credential found", err.Error()) 80 | } 81 | 82 | func TestDefaultChainHasCred(t *testing.T) { 83 | accessKeyIdNew := os.Getenv(EnvVarAccessKeyIdNew) 84 | accessKeyId := os.Getenv(EnvVarAccessKeyId) 85 | accessKeySecret := os.Getenv(EnvVarAccessKeySecret) 86 | os.Unsetenv(EnvVarAccessKeyId) 87 | os.Unsetenv(EnvVarAccessKeySecret) 88 | os.Unsetenv(ENVCredentialFile) 89 | 90 | path, _ := os.Getwd() 91 | oidcTokenFilePathVar := path + "/oidc_token" 92 | roleArn := os.Getenv(ENVRoleArn) 93 | oidcProviderArn := os.Getenv(ENVOIDCProviderArn) 94 | oidcTokenFilePath := os.Getenv(ENVOIDCTokenFile) 95 | roleSessionName := os.Getenv(ENVRoleSessionName) 96 | os.Setenv(ENVRoleArn, "acs:ram::roleArn:role/roleArn") 97 | os.Setenv(ENVOIDCProviderArn, "acs:ram::roleArn") 98 | os.Setenv(ENVOIDCTokenFile, oidcTokenFilePathVar) 99 | os.Setenv(ENVRoleSessionName, "roleSessionName") 100 | defer func() { 101 | os.Setenv(EnvVarAccessKeyIdNew, accessKeyIdNew) 102 | os.Setenv(EnvVarAccessKeyId, accessKeyId) 103 | os.Setenv(EnvVarAccessKeySecret, accessKeySecret) 104 | os.Setenv(ENVRoleArn, roleArn) 105 | os.Setenv(ENVOIDCProviderArn, oidcProviderArn) 106 | os.Setenv(ENVOIDCTokenFile, oidcTokenFilePath) 107 | os.Setenv(ENVRoleSessionName, roleSessionName) 108 | }() 109 | 110 | config, err := defaultChain.resolve() 111 | assert.NotNil(t, config) 112 | assert.Nil(t, err) 113 | assert.Equal(t, "acs:ram::roleArn:role/roleArn", tea.StringValue(config.RoleArn)) 114 | assert.Equal(t, "acs:ram::roleArn", tea.StringValue(config.OIDCProviderArn)) 115 | assert.Equal(t, oidcTokenFilePathVar, tea.StringValue(config.OIDCTokenFilePath)) 116 | assert.Equal(t, "roleSessionName", tea.StringValue(config.RoleSessionName)) 117 | assert.Equal(t, "oidc_role_arn", tea.StringValue(config.Type)) 118 | 119 | os.Setenv("ALIBABA_CLOUD_CLI_PROFILE_DISABLED", "true") 120 | cred, err := NewCredential(nil) 121 | assert.Nil(t, err) 122 | assert.NotNil(t, cred) 123 | assert.Equal(t, "default", *cred.GetType()) 124 | } 125 | -------------------------------------------------------------------------------- /credentials/providers/cli_profile.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path" 10 | "strings" 11 | 12 | "github.com/aliyun/credentials-go/credentials/internal/utils" 13 | ) 14 | 15 | type CLIProfileCredentialsProvider struct { 16 | profileFile string 17 | profileName string 18 | innerProvider CredentialsProvider 19 | } 20 | 21 | type CLIProfileCredentialsProviderBuilder struct { 22 | provider *CLIProfileCredentialsProvider 23 | } 24 | 25 | func (b *CLIProfileCredentialsProviderBuilder) WithProfileFile(profileFile string) *CLIProfileCredentialsProviderBuilder { 26 | b.provider.profileFile = profileFile 27 | return b 28 | } 29 | 30 | func (b *CLIProfileCredentialsProviderBuilder) WithProfileName(profileName string) *CLIProfileCredentialsProviderBuilder { 31 | b.provider.profileName = profileName 32 | return b 33 | } 34 | 35 | func (b *CLIProfileCredentialsProviderBuilder) Build() (provider *CLIProfileCredentialsProvider, err error) { 36 | // 优先级: 37 | // 1. 使用显示指定的 profileFile 38 | // 2. 使用环境变量(ALIBABA_CLOUD_CONFIG_FILE)指定的 profileFile 39 | // 3. 兜底使用 path.Join(homeDir, ".aliyun/config") 作为 profileFile 40 | if b.provider.profileFile == "" { 41 | b.provider.profileFile = os.Getenv("ALIBABA_CLOUD_CONFIG_FILE") 42 | } 43 | // 优先级: 44 | // 1. 使用显示指定的 profileName 45 | // 2. 使用环境变量(ALIBABA_CLOUD_PROFILE)制定的 profileName 46 | // 3. 使用 CLI 配置中的当前 profileName 47 | if b.provider.profileName == "" { 48 | b.provider.profileName = os.Getenv("ALIBABA_CLOUD_PROFILE") 49 | } 50 | 51 | if strings.ToLower(os.Getenv("ALIBABA_CLOUD_CLI_PROFILE_DISABLED")) == "true" { 52 | err = errors.New("the CLI profile is disabled") 53 | return 54 | } 55 | 56 | provider = b.provider 57 | return 58 | } 59 | 60 | func NewCLIProfileCredentialsProviderBuilder() *CLIProfileCredentialsProviderBuilder { 61 | return &CLIProfileCredentialsProviderBuilder{ 62 | provider: &CLIProfileCredentialsProvider{}, 63 | } 64 | } 65 | 66 | type profile struct { 67 | Name string `json:"name"` 68 | Mode string `json:"mode"` 69 | AccessKeyID string `json:"access_key_id"` 70 | AccessKeySecret string `json:"access_key_secret"` 71 | SecurityToken string `json:"sts_token"` 72 | RegionID string `json:"region_id"` 73 | RoleArn string `json:"ram_role_arn"` 74 | RoleSessionName string `json:"ram_session_name"` 75 | DurationSeconds int `json:"expired_seconds"` 76 | StsRegion string `json:"sts_region"` 77 | EnableVpc bool `json:"enable_vpc"` 78 | SourceProfile string `json:"source_profile"` 79 | RoleName string `json:"ram_role_name"` 80 | OIDCTokenFile string `json:"oidc_token_file"` 81 | OIDCProviderARN string `json:"oidc_provider_arn"` 82 | Policy string `json:"policy"` 83 | ExternalId string `json:"external_id"` 84 | } 85 | 86 | type configuration struct { 87 | Current string `json:"current"` 88 | Profiles []*profile `json:"profiles"` 89 | } 90 | 91 | func newConfigurationFromPath(cfgPath string) (conf *configuration, err error) { 92 | bytes, err := ioutil.ReadFile(cfgPath) 93 | if err != nil { 94 | err = fmt.Errorf("reading aliyun cli config from '%s' failed %v", cfgPath, err) 95 | return 96 | } 97 | 98 | conf = &configuration{} 99 | 100 | err = json.Unmarshal(bytes, conf) 101 | if err != nil { 102 | err = fmt.Errorf("unmarshal aliyun cli config from '%s' failed: %s", cfgPath, string(bytes)) 103 | return 104 | } 105 | 106 | if conf.Profiles == nil || len(conf.Profiles) == 0 { 107 | err = fmt.Errorf("no any configured profiles in '%s'", cfgPath) 108 | return 109 | } 110 | 111 | return 112 | } 113 | 114 | func (conf *configuration) getProfile(name string) (profile *profile, err error) { 115 | for _, p := range conf.Profiles { 116 | if p.Name == name { 117 | profile = p 118 | return 119 | } 120 | } 121 | 122 | err = fmt.Errorf("unable to get profile with '%s'", name) 123 | return 124 | } 125 | 126 | func (provider *CLIProfileCredentialsProvider) getCredentialsProvider(conf *configuration, profileName string) (credentialsProvider CredentialsProvider, err error) { 127 | p, err := conf.getProfile(profileName) 128 | if err != nil { 129 | return 130 | } 131 | 132 | switch p.Mode { 133 | case "AK": 134 | credentialsProvider, err = NewStaticAKCredentialsProviderBuilder(). 135 | WithAccessKeyId(p.AccessKeyID). 136 | WithAccessKeySecret(p.AccessKeySecret). 137 | Build() 138 | case "StsToken": 139 | credentialsProvider, err = NewStaticSTSCredentialsProviderBuilder(). 140 | WithAccessKeyId(p.AccessKeyID). 141 | WithAccessKeySecret(p.AccessKeySecret). 142 | WithSecurityToken(p.SecurityToken). 143 | Build() 144 | case "RamRoleArn": 145 | previousProvider, err1 := NewStaticAKCredentialsProviderBuilder(). 146 | WithAccessKeyId(p.AccessKeyID). 147 | WithAccessKeySecret(p.AccessKeySecret). 148 | Build() 149 | if err1 != nil { 150 | return nil, err1 151 | } 152 | 153 | credentialsProvider, err = NewRAMRoleARNCredentialsProviderBuilder(). 154 | WithCredentialsProvider(previousProvider). 155 | WithRoleArn(p.RoleArn). 156 | WithRoleSessionName(p.RoleSessionName). 157 | WithDurationSeconds(p.DurationSeconds). 158 | WithStsRegionId(p.StsRegion). 159 | WithEnableVpc(p.EnableVpc). 160 | WithPolicy(p.Policy). 161 | WithExternalId(p.ExternalId). 162 | Build() 163 | case "EcsRamRole": 164 | credentialsProvider, err = NewECSRAMRoleCredentialsProviderBuilder().WithRoleName(p.RoleName).Build() 165 | case "OIDC": 166 | credentialsProvider, err = NewOIDCCredentialsProviderBuilder(). 167 | WithOIDCTokenFilePath(p.OIDCTokenFile). 168 | WithOIDCProviderARN(p.OIDCProviderARN). 169 | WithRoleArn(p.RoleArn). 170 | WithStsRegionId(p.StsRegion). 171 | WithEnableVpc(p.EnableVpc). 172 | WithDurationSeconds(p.DurationSeconds). 173 | WithRoleSessionName(p.RoleSessionName). 174 | WithPolicy(p.Policy). 175 | Build() 176 | case "ChainableRamRoleArn": 177 | previousProvider, err1 := provider.getCredentialsProvider(conf, p.SourceProfile) 178 | if err1 != nil { 179 | err = fmt.Errorf("get source profile failed: %s", err1.Error()) 180 | return 181 | } 182 | credentialsProvider, err = NewRAMRoleARNCredentialsProviderBuilder(). 183 | WithCredentialsProvider(previousProvider). 184 | WithRoleArn(p.RoleArn). 185 | WithRoleSessionName(p.RoleSessionName). 186 | WithDurationSeconds(p.DurationSeconds). 187 | WithStsRegionId(p.StsRegion). 188 | WithEnableVpc(p.EnableVpc). 189 | WithPolicy(p.Policy). 190 | WithExternalId(p.ExternalId). 191 | Build() 192 | default: 193 | err = fmt.Errorf("unsupported profile mode '%s'", p.Mode) 194 | } 195 | 196 | return 197 | } 198 | 199 | // 默认设置为 GetHomePath,测试时便于 mock 200 | var getHomePath = utils.GetHomePath 201 | 202 | func (provider *CLIProfileCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 203 | if provider.innerProvider == nil { 204 | cfgPath := provider.profileFile 205 | if cfgPath == "" { 206 | homeDir := getHomePath() 207 | if homeDir == "" { 208 | err = fmt.Errorf("cannot found home dir") 209 | return 210 | } 211 | 212 | cfgPath = path.Join(homeDir, ".aliyun/config.json") 213 | } 214 | 215 | conf, err1 := newConfigurationFromPath(cfgPath) 216 | if err1 != nil { 217 | err = err1 218 | return 219 | } 220 | 221 | if provider.profileName == "" { 222 | provider.profileName = conf.Current 223 | } 224 | 225 | provider.innerProvider, err = provider.getCredentialsProvider(conf, provider.profileName) 226 | if err != nil { 227 | return 228 | } 229 | } 230 | 231 | innerCC, err := provider.innerProvider.GetCredentials() 232 | if err != nil { 233 | return 234 | } 235 | 236 | providerName := innerCC.ProviderName 237 | if providerName == "" { 238 | providerName = provider.innerProvider.GetProviderName() 239 | } 240 | 241 | cc = &Credentials{ 242 | AccessKeyId: innerCC.AccessKeyId, 243 | AccessKeySecret: innerCC.AccessKeySecret, 244 | SecurityToken: innerCC.SecurityToken, 245 | ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), providerName), 246 | } 247 | 248 | return 249 | } 250 | 251 | func (provider *CLIProfileCredentialsProvider) GetProviderName() string { 252 | return "cli_profile" 253 | } 254 | -------------------------------------------------------------------------------- /credentials/providers/credentials.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | // 下一版本 Credentials 包 4 | // - 分离 bearer token 5 | // - 从 config 传递迁移到真正的 credentials provider 模式 6 | // - 删除 GetAccessKeyId()/GetAccessKeySecret()/GetSecurityToken() 方法,只保留 GetCredentials() 7 | 8 | // The credentials struct 9 | type Credentials struct { 10 | AccessKeyId string 11 | AccessKeySecret string 12 | SecurityToken string 13 | ProviderName string 14 | } 15 | 16 | // The credentials provider interface, return credentials and provider name 17 | type CredentialsProvider interface { 18 | // Get credentials 19 | GetCredentials() (*Credentials, error) 20 | // Get credentials provider name 21 | GetProviderName() string 22 | } 23 | -------------------------------------------------------------------------------- /credentials/providers/default.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type DefaultCredentialsProvider struct { 10 | providerChain []CredentialsProvider 11 | lastUsedProvider CredentialsProvider 12 | } 13 | 14 | func NewDefaultCredentialsProvider() (provider *DefaultCredentialsProvider) { 15 | providers := []CredentialsProvider{} 16 | 17 | // Add static ak or sts credentials provider 18 | envProvider, err := NewEnvironmentVariableCredentialsProviderBuilder().Build() 19 | if err == nil { 20 | providers = append(providers, envProvider) 21 | } 22 | 23 | // oidc check 24 | oidcProvider, err := NewOIDCCredentialsProviderBuilder().Build() 25 | if err == nil { 26 | providers = append(providers, oidcProvider) 27 | } 28 | 29 | // cli credentials provider 30 | cliProfileProvider, err := NewCLIProfileCredentialsProviderBuilder().Build() 31 | if err == nil { 32 | providers = append(providers, cliProfileProvider) 33 | } 34 | 35 | // profile credentials provider 36 | profileProvider, err := NewProfileCredentialsProviderBuilder().Build() 37 | if err == nil { 38 | providers = append(providers, profileProvider) 39 | } 40 | 41 | // Add IMDS 42 | ecsRamRoleProvider, err := NewECSRAMRoleCredentialsProviderBuilder().Build() 43 | if err == nil { 44 | providers = append(providers, ecsRamRoleProvider) 45 | } 46 | 47 | // credentials uri 48 | if os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI") != "" { 49 | credentialsUriProvider, err := NewURLCredentialsProviderBuilder().Build() 50 | if err == nil { 51 | providers = append(providers, credentialsUriProvider) 52 | } 53 | } 54 | 55 | return &DefaultCredentialsProvider{ 56 | providerChain: providers, 57 | } 58 | } 59 | 60 | func (provider *DefaultCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 61 | if provider.lastUsedProvider != nil { 62 | inner, err1 := provider.lastUsedProvider.GetCredentials() 63 | if err1 != nil { 64 | err = err1 65 | return 66 | } 67 | 68 | providerName := inner.ProviderName 69 | if providerName == "" { 70 | providerName = provider.lastUsedProvider.GetProviderName() 71 | } 72 | 73 | cc = &Credentials{ 74 | AccessKeyId: inner.AccessKeyId, 75 | AccessKeySecret: inner.AccessKeySecret, 76 | SecurityToken: inner.SecurityToken, 77 | ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), providerName), 78 | } 79 | return 80 | } 81 | 82 | errors := []string{} 83 | for _, p := range provider.providerChain { 84 | provider.lastUsedProvider = p 85 | inner, errInLoop := p.GetCredentials() 86 | if errInLoop != nil { 87 | errors = append(errors, errInLoop.Error()) 88 | // 如果有错误,进入下一个获取过程 89 | continue 90 | } 91 | 92 | if inner != nil { 93 | providerName := inner.ProviderName 94 | if providerName == "" { 95 | providerName = p.GetProviderName() 96 | } 97 | cc = &Credentials{ 98 | AccessKeyId: inner.AccessKeyId, 99 | AccessKeySecret: inner.AccessKeySecret, 100 | SecurityToken: inner.SecurityToken, 101 | ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), providerName), 102 | } 103 | return 104 | } 105 | } 106 | 107 | err = fmt.Errorf("unable to get credentials from any of the providers in the chain: %s", strings.Join(errors, ", ")) 108 | return 109 | } 110 | 111 | func (provider *DefaultCredentialsProvider) GetProviderName() string { 112 | return "default" 113 | } 114 | -------------------------------------------------------------------------------- /credentials/providers/default_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path" 7 | "testing" 8 | 9 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 10 | "github.com/aliyun/credentials-go/credentials/internal/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestDefaultCredentialsProvider(t *testing.T) { 15 | provider := NewDefaultCredentialsProvider() 16 | assert.NotNil(t, provider) 17 | assert.Len(t, provider.providerChain, 4) 18 | _, ok := provider.providerChain[0].(*EnvironmentVariableCredentialsProvider) 19 | assert.True(t, ok) 20 | 21 | _, ok = provider.providerChain[1].(*CLIProfileCredentialsProvider) 22 | assert.True(t, ok) 23 | 24 | _, ok = provider.providerChain[2].(*ProfileCredentialsProvider) 25 | assert.True(t, ok) 26 | 27 | _, ok = provider.providerChain[3].(*ECSRAMRoleCredentialsProvider) 28 | assert.True(t, ok) 29 | 30 | // Add oidc provider 31 | rollback := utils.Memory("ALIBABA_CLOUD_OIDC_TOKEN_FILE", 32 | "ALIBABA_CLOUD_OIDC_PROVIDER_ARN", 33 | "ALIBABA_CLOUD_ROLE_ARN", 34 | "ALIBABA_CLOUD_ECS_METADATA", 35 | "ALIBABA_CLOUD_CREDENTIALS_URI") 36 | 37 | defer rollback() 38 | os.Setenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE", "/path/to/oidc.token") 39 | os.Setenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN", "oidcproviderarn") 40 | os.Setenv("ALIBABA_CLOUD_ROLE_ARN", "rolearn") 41 | 42 | provider = NewDefaultCredentialsProvider() 43 | assert.NotNil(t, provider) 44 | assert.Len(t, provider.providerChain, 5) 45 | _, ok = provider.providerChain[0].(*EnvironmentVariableCredentialsProvider) 46 | assert.True(t, ok) 47 | 48 | _, ok = provider.providerChain[1].(*OIDCCredentialsProvider) 49 | assert.True(t, ok) 50 | 51 | _, ok = provider.providerChain[2].(*CLIProfileCredentialsProvider) 52 | assert.True(t, ok) 53 | 54 | _, ok = provider.providerChain[3].(*ProfileCredentialsProvider) 55 | assert.True(t, ok) 56 | 57 | _, ok = provider.providerChain[4].(*ECSRAMRoleCredentialsProvider) 58 | assert.True(t, ok) 59 | 60 | // Add ecs ram role name 61 | os.Setenv("ALIBABA_CLOUD_ECS_METADATA", "rolename") 62 | provider = NewDefaultCredentialsProvider() 63 | assert.NotNil(t, provider) 64 | assert.Len(t, provider.providerChain, 5) 65 | _, ok = provider.providerChain[0].(*EnvironmentVariableCredentialsProvider) 66 | assert.True(t, ok) 67 | 68 | _, ok = provider.providerChain[1].(*OIDCCredentialsProvider) 69 | assert.True(t, ok) 70 | 71 | _, ok = provider.providerChain[2].(*CLIProfileCredentialsProvider) 72 | assert.True(t, ok) 73 | 74 | _, ok = provider.providerChain[3].(*ProfileCredentialsProvider) 75 | assert.True(t, ok) 76 | 77 | _, ok = provider.providerChain[4].(*ECSRAMRoleCredentialsProvider) 78 | assert.True(t, ok) 79 | 80 | // Add ecs ram role 81 | os.Setenv("ALIBABA_CLOUD_CREDENTIALS_URI", "http://") 82 | provider = NewDefaultCredentialsProvider() 83 | assert.NotNil(t, provider) 84 | assert.Len(t, provider.providerChain, 6) 85 | _, ok = provider.providerChain[0].(*EnvironmentVariableCredentialsProvider) 86 | assert.True(t, ok) 87 | 88 | _, ok = provider.providerChain[1].(*OIDCCredentialsProvider) 89 | assert.True(t, ok) 90 | 91 | _, ok = provider.providerChain[2].(*CLIProfileCredentialsProvider) 92 | assert.True(t, ok) 93 | 94 | _, ok = provider.providerChain[3].(*ProfileCredentialsProvider) 95 | assert.True(t, ok) 96 | 97 | _, ok = provider.providerChain[4].(*ECSRAMRoleCredentialsProvider) 98 | assert.True(t, ok) 99 | 100 | _, ok = provider.providerChain[5].(*URLCredentialsProvider) 101 | assert.True(t, ok) 102 | } 103 | 104 | func TestDefaultCredentialsProvider_GetCredentials(t *testing.T) { 105 | rollback := utils.Memory("ALIBABA_CLOUD_ACCESS_KEY_ID", 106 | "ALIBABA_CLOUD_ACCESS_KEY_SECRET", 107 | "ALIBABA_CLOUD_SECURITY_TOKEN", 108 | "ALIBABA_CLOUD_ECS_METADATA_DISABLED", 109 | "ALIBABA_CLOUD_PROFILE") 110 | 111 | defer func() { 112 | getHomePath = utils.GetHomePath 113 | rollback() 114 | }() 115 | originHttpDo := httpDo 116 | defer func() { httpDo = originHttpDo }() 117 | 118 | // testcase: empty home 119 | getHomePath = func() string { 120 | return "" 121 | } 122 | 123 | os.Setenv("ALIBABA_CLOUD_ECS_METADATA_DISABLED", "true") 124 | provider := NewDefaultCredentialsProvider() 125 | assert.Len(t, provider.providerChain, 3) 126 | _, err := provider.GetCredentials() 127 | assert.EqualError(t, err, "unable to get credentials from any of the providers in the chain: unable to get credentials from enviroment variables, Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID), cannot found home dir, cannot found home dir") 128 | 129 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "akid") 130 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "aksecret") 131 | provider = NewDefaultCredentialsProvider() 132 | assert.Len(t, provider.providerChain, 3) 133 | cc, err := provider.GetCredentials() 134 | assert.Nil(t, err) 135 | assert.Equal(t, &Credentials{AccessKeyId: "akid", AccessKeySecret: "aksecret", SecurityToken: "", ProviderName: "default/env"}, cc) 136 | // get again 137 | cc, err = provider.GetCredentials() 138 | assert.Nil(t, err) 139 | assert.Equal(t, &Credentials{AccessKeyId: "akid", AccessKeySecret: "aksecret", SecurityToken: "", ProviderName: "default/env"}, cc) 140 | 141 | getHomePath = func() string { 142 | wd, _ := os.Getwd() 143 | return path.Join(wd, "fixtures") 144 | } 145 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "") 146 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "") 147 | os.Setenv("ALIBABA_CLOUD_PROFILE", "ChainableRamRoleArn") 148 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 149 | res = &httputil.Response{ 150 | StatusCode: 200, 151 | Body: []byte(`{"Credentials": {"AccessKeyId":"akid","AccessKeySecret":"aksecret","Expiration":"2021-10-20T04:27:09Z","SecurityToken":"ststoken"}}`), 152 | } 153 | return 154 | } 155 | provider = NewDefaultCredentialsProvider() 156 | cc, err = provider.GetCredentials() 157 | assert.Nil(t, err) 158 | assert.Equal(t, &Credentials{AccessKeyId: "akid", AccessKeySecret: "aksecret", SecurityToken: "ststoken", ProviderName: "default/cli_profile/ram_role_arn/ram_role_arn/static_ak"}, cc) 159 | 160 | provider.lastUsedProvider = new(testProvider) 161 | cc, err = provider.GetCredentials() 162 | assert.Nil(t, err) 163 | assert.Equal(t, "test", cc.AccessKeyId) 164 | assert.Equal(t, "test", cc.AccessKeySecret) 165 | assert.Equal(t, "default/test", cc.ProviderName) 166 | 167 | provider.lastUsedProvider = new(testErrorProvider) 168 | _, err = provider.GetCredentials() 169 | assert.Equal(t, "error", err.Error()) 170 | } 171 | 172 | type testProvider struct { 173 | } 174 | 175 | func (provider *testProvider) GetCredentials() (cc *Credentials, err error) { 176 | cc = &Credentials{ 177 | AccessKeyId: "test", 178 | AccessKeySecret: "test", 179 | ProviderName: "", 180 | } 181 | return 182 | } 183 | 184 | func (provider *testProvider) GetProviderName() string { 185 | return "test" 186 | } 187 | 188 | type testErrorProvider struct { 189 | } 190 | 191 | func (provider *testErrorProvider) GetCredentials() (cc *Credentials, err error) { 192 | err = errors.New("error") 193 | return 194 | } 195 | 196 | func (provider *testErrorProvider) GetProviderName() string { 197 | return "test" 198 | } 199 | -------------------------------------------------------------------------------- /credentials/providers/ecs_ram_role.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "strconv" 9 | "strings" 10 | "time" 11 | 12 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 13 | ) 14 | 15 | type ECSRAMRoleCredentialsProvider struct { 16 | roleName string 17 | disableIMDSv1 bool 18 | // for sts 19 | session *sessionCredentials 20 | expirationTimestamp int64 21 | // for http options 22 | httpOptions *HttpOptions 23 | } 24 | 25 | type ECSRAMRoleCredentialsProviderBuilder struct { 26 | provider *ECSRAMRoleCredentialsProvider 27 | } 28 | 29 | func NewECSRAMRoleCredentialsProviderBuilder() *ECSRAMRoleCredentialsProviderBuilder { 30 | return &ECSRAMRoleCredentialsProviderBuilder{ 31 | provider: &ECSRAMRoleCredentialsProvider{}, 32 | } 33 | } 34 | 35 | func (builder *ECSRAMRoleCredentialsProviderBuilder) WithRoleName(roleName string) *ECSRAMRoleCredentialsProviderBuilder { 36 | builder.provider.roleName = roleName 37 | return builder 38 | } 39 | 40 | func (builder *ECSRAMRoleCredentialsProviderBuilder) WithDisableIMDSv1(disableIMDSv1 bool) *ECSRAMRoleCredentialsProviderBuilder { 41 | builder.provider.disableIMDSv1 = disableIMDSv1 42 | return builder 43 | } 44 | 45 | func (builder *ECSRAMRoleCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *ECSRAMRoleCredentialsProviderBuilder { 46 | builder.provider.httpOptions = httpOptions 47 | return builder 48 | } 49 | 50 | const defaultMetadataTokenDuration = 21600 // 6 hours 51 | 52 | func (builder *ECSRAMRoleCredentialsProviderBuilder) Build() (provider *ECSRAMRoleCredentialsProvider, err error) { 53 | 54 | if strings.ToLower(os.Getenv("ALIBABA_CLOUD_ECS_METADATA_DISABLED")) == "true" { 55 | err = errors.New("IMDS credentials is disabled") 56 | return 57 | } 58 | 59 | // 设置 roleName 默认值 60 | if builder.provider.roleName == "" { 61 | builder.provider.roleName = os.Getenv("ALIBABA_CLOUD_ECS_METADATA") 62 | } 63 | 64 | if !builder.provider.disableIMDSv1 { 65 | builder.provider.disableIMDSv1 = strings.ToLower(os.Getenv("ALIBABA_CLOUD_IMDSV1_DISABLED")) == "true" 66 | } 67 | 68 | provider = builder.provider 69 | return 70 | } 71 | 72 | type ecsRAMRoleResponse struct { 73 | Code *string `json:"Code"` 74 | AccessKeyId *string `json:"AccessKeyId"` 75 | AccessKeySecret *string `json:"AccessKeySecret"` 76 | SecurityToken *string `json:"SecurityToken"` 77 | LastUpdated *string `json:"LastUpdated"` 78 | Expiration *string `json:"Expiration"` 79 | } 80 | 81 | func (provider *ECSRAMRoleCredentialsProvider) needUpdateCredential() bool { 82 | if provider.expirationTimestamp == 0 { 83 | return true 84 | } 85 | 86 | return provider.expirationTimestamp-time.Now().Unix() <= 180 87 | } 88 | 89 | func (provider *ECSRAMRoleCredentialsProvider) getRoleName() (roleName string, err error) { 90 | req := &httputil.Request{ 91 | Method: "GET", 92 | Protocol: "http", 93 | Host: "100.100.100.200", 94 | Path: "/latest/meta-data/ram/security-credentials/", 95 | Headers: map[string]string{}, 96 | } 97 | 98 | connectTimeout := 1 * time.Second 99 | readTimeout := 1 * time.Second 100 | 101 | if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 102 | connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 103 | } 104 | if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 105 | readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 106 | } 107 | if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 108 | req.Proxy = provider.httpOptions.Proxy 109 | } 110 | req.ConnectTimeout = connectTimeout 111 | req.ReadTimeout = readTimeout 112 | 113 | metadataToken, err := provider.getMetadataToken() 114 | if err != nil { 115 | return "", err 116 | } 117 | if metadataToken != "" { 118 | req.Headers["x-aliyun-ecs-metadata-token"] = metadataToken 119 | } 120 | 121 | res, err := httpDo(req) 122 | if err != nil { 123 | err = fmt.Errorf("get role name failed: %s", err.Error()) 124 | return 125 | } 126 | 127 | if res.StatusCode != 200 { 128 | err = fmt.Errorf("get role name failed: %s %d", req.BuildRequestURL(), res.StatusCode) 129 | return 130 | } 131 | 132 | roleName = strings.TrimSpace(string(res.Body)) 133 | return 134 | } 135 | 136 | func (provider *ECSRAMRoleCredentialsProvider) getCredentials() (session *sessionCredentials, err error) { 137 | roleName := provider.roleName 138 | if roleName == "" { 139 | roleName, err = provider.getRoleName() 140 | if err != nil { 141 | return 142 | } 143 | } 144 | 145 | req := &httputil.Request{ 146 | Method: "GET", 147 | Protocol: "http", 148 | Host: "100.100.100.200", 149 | Path: "/latest/meta-data/ram/security-credentials/" + roleName, 150 | Headers: map[string]string{}, 151 | } 152 | 153 | connectTimeout := 1 * time.Second 154 | readTimeout := 1 * time.Second 155 | 156 | if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 157 | connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 158 | } 159 | if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 160 | readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 161 | } 162 | if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 163 | req.Proxy = provider.httpOptions.Proxy 164 | } 165 | req.ConnectTimeout = connectTimeout 166 | req.ReadTimeout = readTimeout 167 | 168 | metadataToken, err := provider.getMetadataToken() 169 | if err != nil { 170 | return nil, err 171 | } 172 | if metadataToken != "" { 173 | req.Headers["x-aliyun-ecs-metadata-token"] = metadataToken 174 | } 175 | 176 | res, err := httpDo(req) 177 | if err != nil { 178 | err = fmt.Errorf("refresh Ecs sts token err: %s", err.Error()) 179 | return 180 | } 181 | 182 | if res.StatusCode != 200 { 183 | err = fmt.Errorf("refresh Ecs sts token err, httpStatus: %d, message = %s", res.StatusCode, string(res.Body)) 184 | return 185 | } 186 | 187 | var data ecsRAMRoleResponse 188 | err = json.Unmarshal(res.Body, &data) 189 | if err != nil { 190 | err = fmt.Errorf("refresh Ecs sts token err, json.Unmarshal fail: %s", err.Error()) 191 | return 192 | } 193 | 194 | if data.AccessKeyId == nil || data.AccessKeySecret == nil || data.SecurityToken == nil { 195 | err = fmt.Errorf("refresh Ecs sts token err, fail to get credentials") 196 | return 197 | } 198 | 199 | if *data.Code != "Success" { 200 | err = fmt.Errorf("refresh Ecs sts token err, Code is not Success") 201 | return 202 | } 203 | 204 | session = &sessionCredentials{ 205 | AccessKeyId: *data.AccessKeyId, 206 | AccessKeySecret: *data.AccessKeySecret, 207 | SecurityToken: *data.SecurityToken, 208 | Expiration: *data.Expiration, 209 | } 210 | return 211 | } 212 | 213 | func (provider *ECSRAMRoleCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 214 | if provider.session == nil || provider.needUpdateCredential() { 215 | session, err1 := provider.getCredentials() 216 | if err1 != nil { 217 | return nil, err1 218 | } 219 | 220 | provider.session = session 221 | expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", session.Expiration) 222 | if err2 != nil { 223 | return nil, err2 224 | } 225 | provider.expirationTimestamp = expirationTime.Unix() 226 | } 227 | 228 | cc = &Credentials{ 229 | AccessKeyId: provider.session.AccessKeyId, 230 | AccessKeySecret: provider.session.AccessKeySecret, 231 | SecurityToken: provider.session.SecurityToken, 232 | ProviderName: provider.GetProviderName(), 233 | } 234 | return 235 | } 236 | 237 | func (provider *ECSRAMRoleCredentialsProvider) GetProviderName() string { 238 | return "ecs_ram_role" 239 | } 240 | 241 | func (provider *ECSRAMRoleCredentialsProvider) getMetadataToken() (metadataToken string, err error) { 242 | // PUT http://100.100.100.200/latest/api/token 243 | req := &httputil.Request{ 244 | Method: "PUT", 245 | Protocol: "http", 246 | Host: "100.100.100.200", 247 | Path: "/latest/api/token", 248 | Headers: map[string]string{ 249 | "X-aliyun-ecs-metadata-token-ttl-seconds": strconv.Itoa(defaultMetadataTokenDuration), 250 | }, 251 | } 252 | 253 | connectTimeout := 1 * time.Second 254 | readTimeout := 1 * time.Second 255 | 256 | if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 257 | connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 258 | } 259 | if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 260 | readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 261 | } 262 | if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 263 | req.Proxy = provider.httpOptions.Proxy 264 | } 265 | req.ConnectTimeout = connectTimeout 266 | req.ReadTimeout = readTimeout 267 | 268 | res, _err := httpDo(req) 269 | if _err != nil { 270 | if provider.disableIMDSv1 { 271 | err = fmt.Errorf("get metadata token failed: %s", _err.Error()) 272 | } 273 | return 274 | } 275 | if res.StatusCode != 200 { 276 | if provider.disableIMDSv1 { 277 | err = fmt.Errorf("refresh Ecs sts token err, httpStatus: %d, message = %s", res.StatusCode, string(res.Body)) 278 | } 279 | return 280 | } 281 | metadataToken = string(res.Body) 282 | return 283 | } 284 | -------------------------------------------------------------------------------- /credentials/providers/env.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | type EnvironmentVariableCredentialsProvider struct { 9 | } 10 | 11 | type EnvironmentVariableCredentialsProviderBuilder struct { 12 | provider *EnvironmentVariableCredentialsProvider 13 | } 14 | 15 | func NewEnvironmentVariableCredentialsProviderBuilder() *EnvironmentVariableCredentialsProviderBuilder { 16 | return &EnvironmentVariableCredentialsProviderBuilder{ 17 | provider: &EnvironmentVariableCredentialsProvider{}, 18 | } 19 | } 20 | 21 | func (builder *EnvironmentVariableCredentialsProviderBuilder) Build() (provider *EnvironmentVariableCredentialsProvider, err error) { 22 | provider = builder.provider 23 | return 24 | } 25 | 26 | func (provider *EnvironmentVariableCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 27 | accessKeyId := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") 28 | 29 | if accessKeyId == "" { 30 | err = fmt.Errorf("unable to get credentials from enviroment variables, Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID)") 31 | return 32 | } 33 | 34 | accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") 35 | 36 | if accessKeySecret == "" { 37 | err = fmt.Errorf("unable to get credentials from enviroment variables, Access key secret must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_SECRET)") 38 | return 39 | } 40 | 41 | securityToken := os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN") 42 | 43 | cc = &Credentials{ 44 | AccessKeyId: accessKeyId, 45 | AccessKeySecret: accessKeySecret, 46 | SecurityToken: securityToken, 47 | ProviderName: provider.GetProviderName(), 48 | } 49 | 50 | return 51 | } 52 | 53 | func (provider *EnvironmentVariableCredentialsProvider) GetProviderName() string { 54 | return "env" 55 | } 56 | -------------------------------------------------------------------------------- /credentials/providers/env_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/aliyun/credentials-go/credentials/internal/utils" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEnvironmentVariableCredentialsProvider(t *testing.T) { 12 | rollback := utils.Memory("ALIBABA_CLOUD_ACCESS_KEY_ID", "ALIBABA_CLOUD_ACCESS_KEY_SECRET", "ALIBABA_CLOUD_SECURITY_TOKEN") 13 | defer rollback() 14 | 15 | p, err := NewEnvironmentVariableCredentialsProviderBuilder().Build() 16 | assert.Nil(t, err) 17 | _, err = p.GetCredentials() 18 | assert.EqualError(t, err, "unable to get credentials from enviroment variables, Access key ID must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_ID)") 19 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "akid") 20 | _, err = p.GetCredentials() 21 | assert.EqualError(t, err, "unable to get credentials from enviroment variables, Access key secret must be specified via environment variable (ALIBABA_CLOUD_ACCESS_KEY_SECRET)") 22 | 23 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "aksecret") 24 | cc, err := p.GetCredentials() 25 | assert.Nil(t, err) 26 | assert.Equal(t, "akid", cc.AccessKeyId) 27 | assert.Equal(t, "aksecret", cc.AccessKeySecret) 28 | assert.Equal(t, "", cc.SecurityToken) 29 | assert.Equal(t, "env", cc.ProviderName) 30 | 31 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", "token") 32 | cc, err = p.GetCredentials() 33 | assert.Nil(t, err) 34 | assert.Equal(t, "akid", cc.AccessKeyId) 35 | assert.Equal(t, "aksecret", cc.AccessKeySecret) 36 | assert.Equal(t, "token", cc.SecurityToken) 37 | assert.Equal(t, "env", cc.ProviderName) 38 | } 39 | -------------------------------------------------------------------------------- /credentials/providers/fixtures/.alibabacloud/credentials: -------------------------------------------------------------------------------- 1 | [default] 2 | enable = true 3 | type = access_key 4 | access_key_id = foo 5 | access_key_secret = bar 6 | 7 | [notype] 8 | access_key_id = foo 9 | access_key_secret = bar 10 | 11 | [noak] 12 | type = access_key 13 | access_key_secret = bar 14 | 15 | [emptyak] 16 | type = access_key 17 | access_key_id = 18 | access_key_secret = bar 19 | 20 | [ecs] 21 | type = ecs_ram_role 22 | role_name = EcsRamRoleTest 23 | 24 | [noecs] 25 | type = ecs_ram_role 26 | 27 | [emptyecs] 28 | type = ecs_ram_role 29 | role_name = 30 | 31 | [ram] 32 | type = ram_role_arn 33 | access_key_id = foo 34 | access_key_secret = bar 35 | role_arn = role_arn 36 | role_session_name = session_name 37 | 38 | [noram] 39 | type = ram_role_arn 40 | access_key_secret = bar 41 | role_arn = role_arn 42 | role_session_name = session_name 43 | 44 | [emptyram] 45 | type = ram_role_arn 46 | access_key_id = 47 | access_key_secret = bar 48 | role_arn = role_arn 49 | role_session_name = session_name 50 | 51 | [rsa] 52 | type = rsa_key_pair 53 | public_key_id = publicKeyId 54 | private_key_file = ./pk.pem 55 | 56 | [norsa] 57 | type = rsa_key_pair 58 | public_key_id = publicKeyId 59 | 60 | [emptyrsa] 61 | type = rsa_key_pair 62 | public_key_id = publicKeyId 63 | private_key_file = 64 | 65 | [error_rsa] 66 | type = rsa_key_pair 67 | public_key_id = publicKeyId 68 | private_key_file = ./pk_error.pem 69 | 70 | [error_type] 71 | type = error_type 72 | public_key_id = publicKeyId 73 | private_key_file = ./pk_error.pem -------------------------------------------------------------------------------- /credentials/providers/fixtures/.aliyun/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "current": "AK", 3 | "profiles": [ 4 | { 5 | "name": "AK", 6 | "mode": "AK", 7 | "access_key_id": "akid", 8 | "access_key_secret": "secret" 9 | }, 10 | { 11 | "name": "StsToken", 12 | "mode": "StsToken", 13 | "access_key_id": "accessKeyId", 14 | "access_key_secret": "accessKeySecret", 15 | "sts_token": "stsToken" 16 | }, 17 | { 18 | "name": "RamRoleArn", 19 | "mode": "RamRoleArn", 20 | "access_key_id": "akid", 21 | "access_key_secret": "secret", 22 | "ram_role_arn": "arn", 23 | "sts_region": "cn-hangzhou", 24 | "enable_vpc": true, 25 | "policy": "policy", 26 | "external_id": "id" 27 | }, 28 | { 29 | "name": "EcsRamRole", 30 | "mode": "EcsRamRole", 31 | "ram_role_name": "rolename" 32 | }, 33 | { 34 | "name": "OIDC", 35 | "mode": "OIDC", 36 | "ram_role_arn": "role_arn", 37 | "oidc_token_file": "path/to/oidc/file", 38 | "oidc_provider_arn": "provider_arn", 39 | "sts_region": "cn-hangzhou", 40 | "enable_vpc": true, 41 | "policy": "policy" 42 | }, 43 | { 44 | "name": "ChainableRamRoleArn", 45 | "mode": "ChainableRamRoleArn", 46 | "ram_role_arn": "arn", 47 | "source_profile": "RamRoleArn" 48 | }, 49 | { 50 | "name": "ChainableRamRoleArn1", 51 | "mode": "ChainableRamRoleArn", 52 | "source_profile": "AK" 53 | }, 54 | { 55 | "name": "ChainableRamRoleArn2", 56 | "mode": "ChainableRamRoleArn", 57 | "source_profile": "InvalidSource" 58 | }, 59 | { 60 | "name": "get_credentials_error", 61 | "mode": "RamRoleArn", 62 | "access_key_id": "akid", 63 | "access_key_secret": "secret", 64 | "ram_role_arn": "arn" 65 | }, 66 | { 67 | "name": "Unsupported", 68 | "mode": "Unsupported" 69 | } 70 | ] 71 | } -------------------------------------------------------------------------------- /credentials/providers/fixtures/invalid_cli_config.json: -------------------------------------------------------------------------------- 1 | invalid config -------------------------------------------------------------------------------- /credentials/providers/fixtures/mock_cli_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "current": "default", 3 | "profiles": [ 4 | { 5 | "name": "default", 6 | "mode": "AK", 7 | "access_key_id": "akid", 8 | "access_key_secret": "secret" 9 | }, 10 | { 11 | "name": "jacksontian", 12 | "mode": "AK", 13 | "access_key_id": "akid", 14 | "access_key_secret": "secret" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /credentials/providers/fixtures/mock_empty_cli_config.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /credentials/providers/fixtures/mock_oidctoken: -------------------------------------------------------------------------------- 1 | mock oidc token -------------------------------------------------------------------------------- /credentials/providers/hook.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 5 | ) 6 | 7 | var httpDo = httputil.Do 8 | -------------------------------------------------------------------------------- /credentials/providers/oidc.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "time" 13 | 14 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 15 | "github.com/aliyun/credentials-go/credentials/internal/utils" 16 | ) 17 | 18 | type OIDCCredentialsProvider struct { 19 | oidcProviderARN string 20 | oidcTokenFilePath string 21 | roleArn string 22 | roleSessionName string 23 | durationSeconds int 24 | policy string 25 | // for sts endpoint 26 | stsRegionId string 27 | enableVpc bool 28 | stsEndpoint string 29 | 30 | lastUpdateTimestamp int64 31 | expirationTimestamp int64 32 | sessionCredentials *sessionCredentials 33 | // for http options 34 | httpOptions *HttpOptions 35 | } 36 | 37 | type OIDCCredentialsProviderBuilder struct { 38 | provider *OIDCCredentialsProvider 39 | } 40 | 41 | func NewOIDCCredentialsProviderBuilder() *OIDCCredentialsProviderBuilder { 42 | return &OIDCCredentialsProviderBuilder{ 43 | provider: &OIDCCredentialsProvider{}, 44 | } 45 | } 46 | 47 | func (b *OIDCCredentialsProviderBuilder) WithOIDCProviderARN(oidcProviderArn string) *OIDCCredentialsProviderBuilder { 48 | b.provider.oidcProviderARN = oidcProviderArn 49 | return b 50 | } 51 | 52 | func (b *OIDCCredentialsProviderBuilder) WithOIDCTokenFilePath(oidcTokenFilePath string) *OIDCCredentialsProviderBuilder { 53 | b.provider.oidcTokenFilePath = oidcTokenFilePath 54 | return b 55 | } 56 | 57 | func (b *OIDCCredentialsProviderBuilder) WithRoleArn(roleArn string) *OIDCCredentialsProviderBuilder { 58 | b.provider.roleArn = roleArn 59 | return b 60 | } 61 | 62 | func (b *OIDCCredentialsProviderBuilder) WithRoleSessionName(roleSessionName string) *OIDCCredentialsProviderBuilder { 63 | b.provider.roleSessionName = roleSessionName 64 | return b 65 | } 66 | 67 | func (b *OIDCCredentialsProviderBuilder) WithDurationSeconds(durationSeconds int) *OIDCCredentialsProviderBuilder { 68 | b.provider.durationSeconds = durationSeconds 69 | return b 70 | } 71 | 72 | func (b *OIDCCredentialsProviderBuilder) WithStsRegionId(regionId string) *OIDCCredentialsProviderBuilder { 73 | b.provider.stsRegionId = regionId 74 | return b 75 | } 76 | 77 | func (b *OIDCCredentialsProviderBuilder) WithEnableVpc(enableVpc bool) *OIDCCredentialsProviderBuilder { 78 | b.provider.enableVpc = enableVpc 79 | return b 80 | } 81 | 82 | func (b *OIDCCredentialsProviderBuilder) WithPolicy(policy string) *OIDCCredentialsProviderBuilder { 83 | b.provider.policy = policy 84 | return b 85 | } 86 | 87 | func (b *OIDCCredentialsProviderBuilder) WithSTSEndpoint(stsEndpoint string) *OIDCCredentialsProviderBuilder { 88 | b.provider.stsEndpoint = stsEndpoint 89 | return b 90 | } 91 | 92 | func (b *OIDCCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *OIDCCredentialsProviderBuilder { 93 | b.provider.httpOptions = httpOptions 94 | return b 95 | } 96 | 97 | func (b *OIDCCredentialsProviderBuilder) Build() (provider *OIDCCredentialsProvider, err error) { 98 | if b.provider.roleSessionName == "" { 99 | b.provider.roleSessionName = "credentials-go-" + strconv.FormatInt(time.Now().UnixNano()/1000, 10) 100 | } 101 | 102 | if b.provider.oidcTokenFilePath == "" { 103 | b.provider.oidcTokenFilePath = os.Getenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE") 104 | } 105 | 106 | if b.provider.oidcTokenFilePath == "" { 107 | err = errors.New("the OIDCTokenFilePath is empty") 108 | return 109 | } 110 | 111 | if b.provider.oidcProviderARN == "" { 112 | b.provider.oidcProviderARN = os.Getenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN") 113 | } 114 | 115 | if b.provider.oidcProviderARN == "" { 116 | err = errors.New("the OIDCProviderARN is empty") 117 | return 118 | } 119 | 120 | if b.provider.roleArn == "" { 121 | b.provider.roleArn = os.Getenv("ALIBABA_CLOUD_ROLE_ARN") 122 | } 123 | 124 | if b.provider.roleArn == "" { 125 | err = errors.New("the RoleArn is empty") 126 | return 127 | } 128 | 129 | if b.provider.durationSeconds == 0 { 130 | b.provider.durationSeconds = 3600 131 | } 132 | 133 | if b.provider.durationSeconds < 900 { 134 | err = errors.New("the Assume Role session duration should be in the range of 15min - max duration seconds") 135 | } 136 | 137 | if b.provider.stsEndpoint == "" { 138 | if !b.provider.enableVpc { 139 | b.provider.enableVpc = strings.ToLower(os.Getenv("ALIBABA_CLOUD_VPC_ENDPOINT_ENABLED")) == "true" 140 | } 141 | prefix := "sts" 142 | if b.provider.enableVpc { 143 | prefix = "sts-vpc" 144 | } 145 | if b.provider.stsRegionId != "" { 146 | b.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, b.provider.stsRegionId) 147 | } else if region := os.Getenv("ALIBABA_CLOUD_STS_REGION"); region != "" { 148 | b.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, region) 149 | } else { 150 | b.provider.stsEndpoint = "sts.aliyuncs.com" 151 | } 152 | } 153 | 154 | provider = b.provider 155 | return 156 | } 157 | 158 | func (provider *OIDCCredentialsProvider) getCredentials() (session *sessionCredentials, err error) { 159 | req := &httputil.Request{ 160 | Method: "POST", 161 | Protocol: "https", 162 | Host: provider.stsEndpoint, 163 | Headers: map[string]string{}, 164 | } 165 | 166 | connectTimeout := 5 * time.Second 167 | readTimeout := 10 * time.Second 168 | 169 | if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 170 | connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 171 | } 172 | if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 173 | readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 174 | } 175 | if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 176 | req.Proxy = provider.httpOptions.Proxy 177 | } 178 | req.ConnectTimeout = connectTimeout 179 | req.ReadTimeout = readTimeout 180 | 181 | queries := make(map[string]string) 182 | queries["Version"] = "2015-04-01" 183 | queries["Action"] = "AssumeRoleWithOIDC" 184 | queries["Format"] = "JSON" 185 | queries["Timestamp"] = utils.GetTimeInFormatISO8601() 186 | req.Queries = queries 187 | 188 | bodyForm := make(map[string]string) 189 | bodyForm["RoleArn"] = provider.roleArn 190 | bodyForm["OIDCProviderArn"] = provider.oidcProviderARN 191 | token, err := ioutil.ReadFile(provider.oidcTokenFilePath) 192 | if err != nil { 193 | return 194 | } 195 | 196 | bodyForm["OIDCToken"] = string(token) 197 | if provider.policy != "" { 198 | bodyForm["Policy"] = provider.policy 199 | } 200 | 201 | bodyForm["RoleSessionName"] = provider.roleSessionName 202 | bodyForm["DurationSeconds"] = strconv.Itoa(provider.durationSeconds) 203 | req.Form = bodyForm 204 | 205 | // set headers 206 | req.Headers["Accept-Encoding"] = "identity" 207 | res, err := httpDo(req) 208 | if err != nil { 209 | return 210 | } 211 | 212 | if res.StatusCode != http.StatusOK { 213 | message := "get session token failed: " 214 | err = errors.New(message + string(res.Body)) 215 | return 216 | } 217 | var data assumeRoleResponse 218 | err = json.Unmarshal(res.Body, &data) 219 | if err != nil { 220 | err = fmt.Errorf("get oidc sts token err, json.Unmarshal fail: %s", err.Error()) 221 | return 222 | } 223 | if data.Credentials == nil { 224 | err = fmt.Errorf("get oidc sts token err, fail to get credentials") 225 | return 226 | } 227 | 228 | if data.Credentials.AccessKeyId == nil || data.Credentials.AccessKeySecret == nil || data.Credentials.SecurityToken == nil { 229 | err = fmt.Errorf("refresh RoleArn sts token err, fail to get credentials") 230 | return 231 | } 232 | 233 | session = &sessionCredentials{ 234 | AccessKeyId: *data.Credentials.AccessKeyId, 235 | AccessKeySecret: *data.Credentials.AccessKeySecret, 236 | SecurityToken: *data.Credentials.SecurityToken, 237 | Expiration: *data.Credentials.Expiration, 238 | } 239 | return 240 | } 241 | 242 | func (provider *OIDCCredentialsProvider) needUpdateCredential() (result bool) { 243 | if provider.expirationTimestamp == 0 { 244 | return true 245 | } 246 | 247 | return provider.expirationTimestamp-time.Now().Unix() <= 180 248 | } 249 | 250 | func (provider *OIDCCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 251 | if provider.sessionCredentials == nil || provider.needUpdateCredential() { 252 | sessionCredentials, err1 := provider.getCredentials() 253 | if err1 != nil { 254 | return nil, err1 255 | } 256 | 257 | provider.sessionCredentials = sessionCredentials 258 | expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration) 259 | if err2 != nil { 260 | return nil, err2 261 | } 262 | 263 | provider.lastUpdateTimestamp = time.Now().Unix() 264 | provider.expirationTimestamp = expirationTime.Unix() 265 | } 266 | 267 | cc = &Credentials{ 268 | AccessKeyId: provider.sessionCredentials.AccessKeyId, 269 | AccessKeySecret: provider.sessionCredentials.AccessKeySecret, 270 | SecurityToken: provider.sessionCredentials.SecurityToken, 271 | ProviderName: provider.GetProviderName(), 272 | } 273 | return 274 | } 275 | 276 | func (provider *OIDCCredentialsProvider) GetProviderName() string { 277 | return "oidc_role_arn" 278 | } 279 | -------------------------------------------------------------------------------- /credentials/providers/profile.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path" 8 | 9 | "github.com/aliyun/credentials-go/credentials/internal/utils" 10 | "gopkg.in/ini.v1" 11 | ) 12 | 13 | type ProfileCredentialsProvider struct { 14 | profileName string 15 | innerProvider CredentialsProvider 16 | } 17 | 18 | type ProfileCredentialsProviderBuilder struct { 19 | provider *ProfileCredentialsProvider 20 | } 21 | 22 | func NewProfileCredentialsProviderBuilder() (builder *ProfileCredentialsProviderBuilder) { 23 | return &ProfileCredentialsProviderBuilder{ 24 | provider: &ProfileCredentialsProvider{}, 25 | } 26 | } 27 | 28 | func (b *ProfileCredentialsProviderBuilder) WithProfileName(profileName string) *ProfileCredentialsProviderBuilder { 29 | b.provider.profileName = profileName 30 | return b 31 | } 32 | 33 | func (b *ProfileCredentialsProviderBuilder) Build() (provider *ProfileCredentialsProvider, err error) { 34 | // 优先级: 35 | // 1. 使用显示指定的 profileName 36 | // 2. 使用环境变量(ALIBABA_CLOUD_PROFILE)指定的 profileName 37 | // 3. 兜底使用 default 作为 profileName 38 | b.provider.profileName = utils.GetDefaultString(b.provider.profileName, os.Getenv("ALIBABA_CLOUD_PROFILE"), "default") 39 | 40 | provider = b.provider 41 | return 42 | } 43 | 44 | func (provider *ProfileCredentialsProvider) getCredentialsProvider(ini *ini.File) (credentialsProvider CredentialsProvider, err error) { 45 | section, err := ini.GetSection(provider.profileName) 46 | if err != nil { 47 | err = errors.New("ERROR: Can not load section" + err.Error()) 48 | return 49 | } 50 | 51 | value, err := section.GetKey("type") 52 | if err != nil { 53 | err = errors.New("ERROR: Can not find credential type" + err.Error()) 54 | return 55 | } 56 | 57 | switch value.String() { 58 | case "access_key": 59 | value1, err1 := section.GetKey("access_key_id") 60 | value2, err2 := section.GetKey("access_key_secret") 61 | if err1 != nil || err2 != nil { 62 | err = errors.New("ERROR: Failed to get value") 63 | return 64 | } 65 | 66 | if value1.String() == "" || value2.String() == "" { 67 | err = errors.New("ERROR: Value can't be empty") 68 | return 69 | } 70 | 71 | credentialsProvider, err = NewStaticAKCredentialsProviderBuilder(). 72 | WithAccessKeyId(value1.String()). 73 | WithAccessKeySecret(value2.String()). 74 | Build() 75 | case "ecs_ram_role": 76 | value1, err1 := section.GetKey("role_name") 77 | if err1 != nil { 78 | err = errors.New("ERROR: Failed to get value") 79 | return 80 | } 81 | credentialsProvider, err = NewECSRAMRoleCredentialsProviderBuilder().WithRoleName(value1.String()).Build() 82 | case "ram_role_arn": 83 | value1, err1 := section.GetKey("access_key_id") 84 | value2, err2 := section.GetKey("access_key_secret") 85 | value3, err3 := section.GetKey("role_arn") 86 | value4, err4 := section.GetKey("role_session_name") 87 | if err1 != nil || err2 != nil || err3 != nil || err4 != nil { 88 | err = errors.New("ERROR: Failed to get value") 89 | return 90 | } 91 | if value1.String() == "" || value2.String() == "" || value3.String() == "" || value4.String() == "" { 92 | err = errors.New("ERROR: Value can't be empty") 93 | return 94 | } 95 | previous, err5 := NewStaticAKCredentialsProviderBuilder(). 96 | WithAccessKeyId(value1.String()). 97 | WithAccessKeySecret(value2.String()). 98 | Build() 99 | if err5 != nil { 100 | err = errors.New("get previous credentials provider failed") 101 | return 102 | } 103 | rawPolicy, _ := section.GetKey("policy") 104 | policy := "" 105 | if rawPolicy != nil { 106 | policy = rawPolicy.String() 107 | } 108 | 109 | credentialsProvider, err = NewRAMRoleARNCredentialsProviderBuilder(). 110 | WithCredentialsProvider(previous). 111 | WithRoleArn(value3.String()). 112 | WithRoleSessionName(value4.String()). 113 | WithPolicy(policy). 114 | WithDurationSeconds(3600). 115 | Build() 116 | default: 117 | err = errors.New("ERROR: Failed to get credential") 118 | } 119 | return 120 | } 121 | 122 | func (provider *ProfileCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 123 | if provider.innerProvider == nil { 124 | sharedCfgPath := os.Getenv("ALIBABA_CLOUD_CREDENTIALS_FILE") 125 | if sharedCfgPath == "" { 126 | homeDir := getHomePath() 127 | if homeDir == "" { 128 | err = fmt.Errorf("cannot found home dir") 129 | return 130 | } 131 | 132 | sharedCfgPath = path.Join(homeDir, ".alibabacloud/credentials") 133 | } 134 | 135 | ini, err1 := ini.Load(sharedCfgPath) 136 | if err1 != nil { 137 | err = errors.New("ERROR: Can not open file" + err1.Error()) 138 | return 139 | } 140 | 141 | provider.innerProvider, err = provider.getCredentialsProvider(ini) 142 | if err != nil { 143 | return 144 | } 145 | } 146 | 147 | innerCC, err := provider.innerProvider.GetCredentials() 148 | if err != nil { 149 | return 150 | } 151 | 152 | providerName := innerCC.ProviderName 153 | if providerName == "" { 154 | providerName = provider.innerProvider.GetProviderName() 155 | } 156 | 157 | cc = &Credentials{ 158 | AccessKeyId: innerCC.AccessKeyId, 159 | AccessKeySecret: innerCC.AccessKeySecret, 160 | SecurityToken: innerCC.SecurityToken, 161 | ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), providerName), 162 | } 163 | 164 | return 165 | } 166 | 167 | func (provider *ProfileCredentialsProvider) GetProviderName() string { 168 | return "profile" 169 | } 170 | -------------------------------------------------------------------------------- /credentials/providers/profile_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "testing" 7 | 8 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 9 | "github.com/aliyun/credentials-go/credentials/internal/utils" 10 | "github.com/stretchr/testify/assert" 11 | "gopkg.in/ini.v1" 12 | ) 13 | 14 | var inistr = ` 15 | [default] 16 | enable = true 17 | type = access_key 18 | access_key_id = foo 19 | access_key_secret = bar 20 | 21 | [notype] 22 | access_key_id = foo 23 | access_key_secret = bar 24 | 25 | [noak] 26 | type = access_key 27 | access_key_secret = bar 28 | 29 | [emptyak] 30 | type = access_key 31 | access_key_id = 32 | access_key_secret = bar 33 | 34 | [ecs] 35 | type = ecs_ram_role 36 | role_name = EcsRamRoleTest 37 | 38 | [noecs] 39 | type = ecs_ram_role 40 | 41 | [emptyecs] 42 | type = ecs_ram_role 43 | role_name = 44 | 45 | [ram] 46 | type = ram_role_arn 47 | access_key_id = foo 48 | access_key_secret = bar 49 | role_arn = role_arn 50 | role_session_name = session_name 51 | policy = {"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"} 52 | 53 | [noram] 54 | type = ram_role_arn 55 | access_key_secret = bar 56 | role_arn = role_arn 57 | role_session_name = session_name 58 | 59 | [emptyram] 60 | type = ram_role_arn 61 | access_key_id = 62 | access_key_secret = bar 63 | role_arn = role_arn 64 | role_session_name = session_name 65 | 66 | [rsa] 67 | type = rsa_key_pair 68 | public_key_id = publicKeyId 69 | private_key_file = ./pk.pem 70 | 71 | [norsa] 72 | type = rsa_key_pair 73 | public_key_id = publicKeyId 74 | 75 | [emptyrsa] 76 | type = rsa_key_pair 77 | public_key_id = publicKeyId 78 | private_key_file = 79 | 80 | [error_rsa] 81 | type = rsa_key_pair 82 | public_key_id = publicKeyId 83 | private_key_file = ./pk_error.pem 84 | 85 | [error_type] 86 | type = error_type 87 | public_key_id = publicKeyId 88 | private_key_file = ./pk_error.pem 89 | ` 90 | 91 | func TestProfileCredentialsProviderBuilder(t *testing.T) { 92 | rollback := utils.Memory("ALIBABA_CLOUD_PROFILE") 93 | defer rollback() 94 | 95 | // profile name from specified 96 | provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 97 | assert.Nil(t, err) 98 | assert.Equal(t, "custom", provider.profileName) 99 | 100 | // profile name from env 101 | os.Setenv("ALIBABA_CLOUD_PROFILE", "profile_from_env") 102 | provider, err = NewProfileCredentialsProviderBuilder().Build() 103 | assert.Nil(t, err) 104 | 105 | assert.Equal(t, "profile_from_env", provider.profileName) 106 | 107 | // profile name from default 108 | os.Setenv("ALIBABA_CLOUD_PROFILE", "") 109 | provider, err = NewProfileCredentialsProviderBuilder().Build() 110 | assert.Nil(t, err) 111 | assert.Equal(t, "default", provider.profileName) 112 | } 113 | 114 | func TestProfileCredentialsProvider_getCredentialsProvider(t *testing.T) { 115 | provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 116 | assert.Nil(t, err) 117 | _, err = provider.getCredentialsProvider(ini.Empty()) 118 | assert.NotNil(t, err) 119 | assert.EqualError(t, err, "ERROR: Can not load sectionsection \"custom\" does not exist") 120 | 121 | file, err := ini.Load([]byte(inistr)) 122 | assert.Nil(t, err) 123 | assert.NotNil(t, file) 124 | 125 | // no type 126 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("notype").Build() 127 | assert.Nil(t, err) 128 | _, err = provider.getCredentialsProvider(file) 129 | assert.NotNil(t, err) 130 | assert.EqualError(t, err, "ERROR: Can not find credential typeerror when getting key of section \"notype\": key \"type\" not exists") 131 | 132 | // no ak 133 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noak").Build() 134 | assert.Nil(t, err) 135 | _, err = provider.getCredentialsProvider(file) 136 | assert.NotNil(t, err) 137 | assert.EqualError(t, err, "ERROR: Failed to get value") 138 | 139 | // value is empty 140 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("emptyak").Build() 141 | assert.Nil(t, err) 142 | _, err = provider.getCredentialsProvider(file) 143 | assert.NotNil(t, err) 144 | assert.EqualError(t, err, "ERROR: Value can't be empty") 145 | 146 | // static ak provider 147 | provider, err = NewProfileCredentialsProviderBuilder().Build() 148 | assert.Nil(t, err) 149 | cp, err := provider.getCredentialsProvider(file) 150 | assert.Nil(t, err) 151 | akcp, ok := cp.(*StaticAKCredentialsProvider) 152 | assert.True(t, ok) 153 | cc, err := akcp.GetCredentials() 154 | assert.Nil(t, err) 155 | assert.Equal(t, &Credentials{AccessKeyId: "foo", AccessKeySecret: "bar", SecurityToken: "", ProviderName: "static_ak"}, cc) 156 | 157 | // ecs_ram_role without rolename 158 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noecs").Build() 159 | assert.Nil(t, err) 160 | _, err = provider.getCredentialsProvider(file) 161 | assert.EqualError(t, err, "ERROR: Failed to get value") 162 | 163 | // ecs_ram_role with rolename 164 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("ecs").Build() 165 | assert.Nil(t, err) 166 | cp, err = provider.getCredentialsProvider(file) 167 | assert.Nil(t, err) 168 | _, ok = cp.(*ECSRAMRoleCredentialsProvider) 169 | assert.True(t, ok) 170 | 171 | // ram role arn without keys 172 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noram").Build() 173 | assert.Nil(t, err) 174 | _, err = provider.getCredentialsProvider(file) 175 | assert.EqualError(t, err, "ERROR: Failed to get value") 176 | 177 | // ram role arn without values 178 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("emptyram").Build() 179 | assert.Nil(t, err) 180 | _, err = provider.getCredentialsProvider(file) 181 | assert.EqualError(t, err, "ERROR: Value can't be empty") 182 | 183 | // normal ram role arn 184 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("ram").Build() 185 | assert.Nil(t, err) 186 | cp, err = provider.getCredentialsProvider(file) 187 | assert.Nil(t, err) 188 | _, ok = cp.(*RAMRoleARNCredentialsProvider) 189 | assert.True(t, ok) 190 | 191 | // unsupported type 192 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("error_type").Build() 193 | assert.Nil(t, err) 194 | _, err = provider.getCredentialsProvider(file) 195 | assert.EqualError(t, err, "ERROR: Failed to get credential") 196 | } 197 | 198 | func TestProfileCredentialsProviderGetCredentials(t *testing.T) { 199 | originHttpDo := httpDo 200 | defer func() { httpDo = originHttpDo }() 201 | rollback := utils.Memory("ALIBABA_CLOUD_CREDENTIALS_FILE") 202 | defer func() { 203 | getHomePath = utils.GetHomePath 204 | rollback() 205 | }() 206 | 207 | // testcase: empty home 208 | getHomePath = func() string { 209 | return "" 210 | } 211 | provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 212 | assert.Nil(t, err) 213 | _, err = provider.GetCredentials() 214 | assert.EqualError(t, err, "cannot found home dir") 215 | 216 | // testcase: invalid home 217 | getHomePath = func() string { 218 | return "/path/invalid/home/dir" 219 | } 220 | 221 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 222 | assert.Nil(t, err) 223 | _, err = provider.GetCredentials() 224 | assert.EqualError(t, err, "ERROR: Can not open fileopen /path/invalid/home/dir/.alibabacloud/credentials: no such file or directory") 225 | 226 | // testcase: specify credentials file with env 227 | os.Setenv("ALIBABA_CLOUD_CREDENTIALS_FILE", "/path/to/credentials.invalid") 228 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 229 | assert.Nil(t, err) 230 | _, err = provider.GetCredentials() 231 | assert.EqualError(t, err, "ERROR: Can not open fileopen /path/to/credentials.invalid: no such file or directory") 232 | os.Unsetenv("ALIBABA_CLOUD_CREDENTIALS_FILE") 233 | 234 | // get from credentials file 235 | getHomePath = func() string { 236 | wd, _ := os.Getwd() 237 | return path.Join(wd, "fixtures") 238 | } 239 | 240 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build() 241 | assert.Nil(t, err) 242 | _, err = provider.GetCredentials() 243 | assert.EqualError(t, err, "ERROR: Can not load sectionsection \"custom\" does not exist") 244 | 245 | provider, err = NewProfileCredentialsProviderBuilder().Build() 246 | assert.Nil(t, err) 247 | cc, err := provider.GetCredentials() 248 | assert.Nil(t, err) 249 | assert.Equal(t, &Credentials{AccessKeyId: "foo", AccessKeySecret: "bar", SecurityToken: "", ProviderName: "profile/static_ak"}, cc) 250 | 251 | // get credentials again 252 | cc, err = provider.GetCredentials() 253 | assert.Nil(t, err) 254 | assert.Equal(t, &Credentials{AccessKeyId: "foo", AccessKeySecret: "bar", SecurityToken: "", ProviderName: "profile/static_ak"}, cc) 255 | 256 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 257 | res = &httputil.Response{ 258 | StatusCode: 200, 259 | Body: []byte(`{"Credentials": {"AccessKeyId":"akid","AccessKeySecret":"aksecret","Expiration":"2021-10-20T04:27:09Z","SecurityToken":"ststoken"}}`), 260 | } 261 | return 262 | } 263 | provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("ram").Build() 264 | assert.Nil(t, err) 265 | cc, err = provider.GetCredentials() 266 | assert.Nil(t, err) 267 | assert.Equal(t, "akid", cc.AccessKeyId) 268 | assert.Equal(t, "aksecret", cc.AccessKeySecret) 269 | assert.Equal(t, "ststoken", cc.SecurityToken) 270 | assert.Equal(t, "profile/ram_role_arn/static_ak", cc.ProviderName) 271 | 272 | provider.innerProvider = new(testProvider) 273 | cc, err = provider.GetCredentials() 274 | assert.Nil(t, err) 275 | assert.Equal(t, "test", cc.AccessKeyId) 276 | assert.Equal(t, "test", cc.AccessKeySecret) 277 | assert.Equal(t, "profile/test", cc.ProviderName) 278 | 279 | provider.innerProvider = new(testErrorProvider) 280 | _, err = provider.GetCredentials() 281 | assert.Equal(t, "error", err.Error()) 282 | } 283 | -------------------------------------------------------------------------------- /credentials/providers/static_ak.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | type StaticAKCredentialsProvider struct { 9 | accessKeyId string 10 | accessKeySecret string 11 | } 12 | 13 | type StaticAKCredentialsProviderBuilder struct { 14 | provider *StaticAKCredentialsProvider 15 | } 16 | 17 | func NewStaticAKCredentialsProviderBuilder() *StaticAKCredentialsProviderBuilder { 18 | return &StaticAKCredentialsProviderBuilder{ 19 | provider: &StaticAKCredentialsProvider{}, 20 | } 21 | } 22 | 23 | func (builder *StaticAKCredentialsProviderBuilder) WithAccessKeyId(accessKeyId string) *StaticAKCredentialsProviderBuilder { 24 | builder.provider.accessKeyId = accessKeyId 25 | return builder 26 | } 27 | 28 | func (builder *StaticAKCredentialsProviderBuilder) WithAccessKeySecret(accessKeySecret string) *StaticAKCredentialsProviderBuilder { 29 | builder.provider.accessKeySecret = accessKeySecret 30 | return builder 31 | } 32 | 33 | func (builder *StaticAKCredentialsProviderBuilder) Build() (provider *StaticAKCredentialsProvider, err error) { 34 | if builder.provider.accessKeyId == "" { 35 | builder.provider.accessKeyId = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") 36 | } 37 | 38 | if builder.provider.accessKeyId == "" { 39 | err = errors.New("the access key id is empty") 40 | return 41 | } 42 | 43 | if builder.provider.accessKeySecret == "" { 44 | builder.provider.accessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") 45 | } 46 | 47 | if builder.provider.accessKeySecret == "" { 48 | err = errors.New("the access key secret is empty") 49 | return 50 | } 51 | 52 | provider = builder.provider 53 | return 54 | } 55 | 56 | func (provider *StaticAKCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 57 | cc = &Credentials{ 58 | AccessKeyId: provider.accessKeyId, 59 | AccessKeySecret: provider.accessKeySecret, 60 | ProviderName: provider.GetProviderName(), 61 | } 62 | return 63 | } 64 | 65 | func (provider *StaticAKCredentialsProvider) GetProviderName() string { 66 | return "static_ak" 67 | } 68 | -------------------------------------------------------------------------------- /credentials/providers/static_ak_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestStaticAKCredentialsProvider(t *testing.T) { 11 | _, err := NewStaticAKCredentialsProviderBuilder(). 12 | Build() 13 | assert.EqualError(t, err, "the access key id is empty") 14 | 15 | _, err = NewStaticAKCredentialsProviderBuilder(). 16 | WithAccessKeyId("akid"). 17 | Build() 18 | assert.EqualError(t, err, "the access key secret is empty") 19 | 20 | provider, err := NewStaticAKCredentialsProviderBuilder(). 21 | WithAccessKeyId("accessKeyId"). 22 | WithAccessKeySecret("accessKeySecret"). 23 | Build() 24 | assert.Nil(t, err) 25 | assert.Equal(t, "static_ak", provider.GetProviderName()) 26 | 27 | cred, err := provider.GetCredentials() 28 | assert.Nil(t, err) 29 | assert.Equal(t, "accessKeyId", cred.AccessKeyId) 30 | assert.Equal(t, "accessKeySecret", cred.AccessKeySecret) 31 | assert.Equal(t, "", cred.SecurityToken) 32 | assert.Equal(t, "static_ak", cred.ProviderName) 33 | } 34 | 35 | func TestStaticAKCredentialsProviderWithEnv(t *testing.T) { 36 | originAKID := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") 37 | originAKSecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") 38 | defer func() { 39 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", originAKID) 40 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", originAKSecret) 41 | }() 42 | 43 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "akid_from_env") 44 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "aksecret_from_env") 45 | provider, err := NewStaticAKCredentialsProviderBuilder(). 46 | Build() 47 | assert.Nil(t, err) 48 | assert.Equal(t, "static_ak", provider.GetProviderName()) 49 | 50 | cred, err := provider.GetCredentials() 51 | assert.Nil(t, err) 52 | assert.Equal(t, "akid_from_env", cred.AccessKeyId) 53 | assert.Equal(t, "aksecret_from_env", cred.AccessKeySecret) 54 | assert.Equal(t, "", cred.SecurityToken) 55 | assert.Equal(t, "static_ak", cred.ProviderName) 56 | } 57 | -------------------------------------------------------------------------------- /credentials/providers/static_sts.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | type StaticSTSCredentialsProvider struct { 9 | accessKeyId string 10 | accessKeySecret string 11 | securityToken string 12 | } 13 | 14 | type StaticSTSCredentialsProviderBuilder struct { 15 | provider *StaticSTSCredentialsProvider 16 | } 17 | 18 | func NewStaticSTSCredentialsProviderBuilder() *StaticSTSCredentialsProviderBuilder { 19 | return &StaticSTSCredentialsProviderBuilder{ 20 | provider: &StaticSTSCredentialsProvider{}, 21 | } 22 | } 23 | 24 | func (builder *StaticSTSCredentialsProviderBuilder) WithAccessKeyId(accessKeyId string) *StaticSTSCredentialsProviderBuilder { 25 | builder.provider.accessKeyId = accessKeyId 26 | return builder 27 | } 28 | 29 | func (builder *StaticSTSCredentialsProviderBuilder) WithAccessKeySecret(accessKeySecret string) *StaticSTSCredentialsProviderBuilder { 30 | builder.provider.accessKeySecret = accessKeySecret 31 | return builder 32 | } 33 | 34 | func (builder *StaticSTSCredentialsProviderBuilder) WithSecurityToken(securityToken string) *StaticSTSCredentialsProviderBuilder { 35 | builder.provider.securityToken = securityToken 36 | return builder 37 | } 38 | 39 | func (builder *StaticSTSCredentialsProviderBuilder) Build() (provider *StaticSTSCredentialsProvider, err error) { 40 | if builder.provider.accessKeyId == "" { 41 | builder.provider.accessKeyId = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") 42 | } 43 | 44 | if builder.provider.accessKeyId == "" { 45 | err = errors.New("the access key id is empty") 46 | return 47 | } 48 | 49 | if builder.provider.accessKeySecret == "" { 50 | builder.provider.accessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") 51 | } 52 | 53 | if builder.provider.accessKeySecret == "" { 54 | err = errors.New("the access key secret is empty") 55 | return 56 | } 57 | 58 | if builder.provider.securityToken == "" { 59 | builder.provider.securityToken = os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN") 60 | } 61 | 62 | if builder.provider.securityToken == "" { 63 | err = errors.New("the security token is empty") 64 | return 65 | } 66 | 67 | provider = builder.provider 68 | return 69 | } 70 | 71 | func (provider *StaticSTSCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 72 | cc = &Credentials{ 73 | AccessKeyId: provider.accessKeyId, 74 | AccessKeySecret: provider.accessKeySecret, 75 | SecurityToken: provider.securityToken, 76 | ProviderName: provider.GetProviderName(), 77 | } 78 | return 79 | } 80 | 81 | func (provider *StaticSTSCredentialsProvider) GetProviderName() string { 82 | return "static_sts" 83 | } 84 | -------------------------------------------------------------------------------- /credentials/providers/static_sts_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestStaticSTSCredentialsProvider(t *testing.T) { 11 | _, err := NewStaticSTSCredentialsProviderBuilder(). 12 | Build() 13 | assert.EqualError(t, err, "the access key id is empty") 14 | 15 | _, err = NewStaticSTSCredentialsProviderBuilder(). 16 | WithAccessKeyId("akid"). 17 | Build() 18 | assert.EqualError(t, err, "the access key secret is empty") 19 | 20 | _, err = NewStaticSTSCredentialsProviderBuilder(). 21 | WithAccessKeyId("akid"). 22 | WithAccessKeySecret("aksecret"). 23 | Build() 24 | assert.EqualError(t, err, "the security token is empty") 25 | 26 | provider, err := NewStaticSTSCredentialsProviderBuilder(). 27 | WithAccessKeyId("accessKeyId"). 28 | WithAccessKeySecret("accessKeySecret"). 29 | WithSecurityToken("securityToken"). 30 | Build() 31 | assert.Nil(t, err) 32 | assert.Equal(t, "static_sts", provider.GetProviderName()) 33 | 34 | cred, err := provider.GetCredentials() 35 | assert.Nil(t, err) 36 | assert.Equal(t, "accessKeyId", cred.AccessKeyId) 37 | assert.Equal(t, "accessKeySecret", cred.AccessKeySecret) 38 | assert.Equal(t, "securityToken", cred.SecurityToken) 39 | assert.Equal(t, "static_sts", cred.ProviderName) 40 | } 41 | 42 | func TestStaticSTSCredentialsProviderWithEnv(t *testing.T) { 43 | originAKID := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") 44 | originAKSecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") 45 | originToken := os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN") 46 | defer func() { 47 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", originAKID) 48 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", originAKSecret) 49 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", originToken) 50 | }() 51 | 52 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "akid_from_env") 53 | os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "aksecret_from_env") 54 | os.Setenv("ALIBABA_CLOUD_SECURITY_TOKEN", "token_from_env") 55 | provider, err := NewStaticSTSCredentialsProviderBuilder(). 56 | Build() 57 | assert.Nil(t, err) 58 | assert.Equal(t, "static_sts", provider.GetProviderName()) 59 | 60 | cred, err := provider.GetCredentials() 61 | assert.Nil(t, err) 62 | assert.Equal(t, "akid_from_env", cred.AccessKeyId) 63 | assert.Equal(t, "aksecret_from_env", cred.AccessKeySecret) 64 | assert.Equal(t, "token_from_env", cred.SecurityToken) 65 | assert.Equal(t, "static_sts", cred.ProviderName) 66 | } 67 | -------------------------------------------------------------------------------- /credentials/providers/uri.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | "time" 10 | 11 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 12 | ) 13 | 14 | type URLCredentialsProvider struct { 15 | url string 16 | // for sts 17 | sessionCredentials *sessionCredentials 18 | // for http options 19 | httpOptions *HttpOptions 20 | // inner 21 | expirationTimestamp int64 22 | } 23 | 24 | type URLCredentialsProviderBuilder struct { 25 | provider *URLCredentialsProvider 26 | } 27 | 28 | func NewURLCredentialsProviderBuilder() *URLCredentialsProviderBuilder { 29 | return &URLCredentialsProviderBuilder{ 30 | provider: &URLCredentialsProvider{}, 31 | } 32 | } 33 | 34 | func (builder *URLCredentialsProviderBuilder) WithUrl(url string) *URLCredentialsProviderBuilder { 35 | builder.provider.url = url 36 | return builder 37 | } 38 | 39 | func (builder *URLCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *URLCredentialsProviderBuilder { 40 | builder.provider.httpOptions = httpOptions 41 | return builder 42 | } 43 | 44 | func (builder *URLCredentialsProviderBuilder) Build() (provider *URLCredentialsProvider, err error) { 45 | 46 | if builder.provider.url == "" { 47 | builder.provider.url = os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI") 48 | } 49 | 50 | if builder.provider.url == "" { 51 | err = errors.New("the url is empty") 52 | return 53 | } 54 | 55 | provider = builder.provider 56 | return 57 | } 58 | 59 | type urlResponse struct { 60 | AccessKeyId *string `json:"AccessKeyId"` 61 | AccessKeySecret *string `json:"AccessKeySecret"` 62 | SecurityToken *string `json:"SecurityToken"` 63 | Expiration *string `json:"Expiration"` 64 | } 65 | 66 | func (provider *URLCredentialsProvider) getCredentials() (session *sessionCredentials, err error) { 67 | req := &httputil.Request{ 68 | Method: "GET", 69 | URL: provider.url, 70 | } 71 | 72 | connectTimeout := 5 * time.Second 73 | readTimeout := 10 * time.Second 74 | 75 | if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 76 | connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 77 | } 78 | if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 79 | readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 80 | } 81 | if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 82 | req.Proxy = provider.httpOptions.Proxy 83 | } 84 | req.ConnectTimeout = connectTimeout 85 | req.ReadTimeout = readTimeout 86 | 87 | res, err := httpDo(req) 88 | if err != nil { 89 | return 90 | } 91 | 92 | if res.StatusCode != http.StatusOK { 93 | err = fmt.Errorf("get credentials from %s failed: %s", req.BuildRequestURL(), string(res.Body)) 94 | return 95 | } 96 | 97 | var resp urlResponse 98 | err = json.Unmarshal(res.Body, &resp) 99 | if err != nil { 100 | err = fmt.Errorf("get credentials from %s failed with error, json unmarshal fail: %s", req.BuildRequestURL(), err.Error()) 101 | return 102 | } 103 | 104 | if resp.AccessKeyId == nil || resp.AccessKeySecret == nil || resp.SecurityToken == nil || resp.Expiration == nil { 105 | err = fmt.Errorf("refresh credentials from %s failed: %s", req.BuildRequestURL(), string(res.Body)) 106 | return 107 | } 108 | 109 | session = &sessionCredentials{ 110 | AccessKeyId: *resp.AccessKeyId, 111 | AccessKeySecret: *resp.AccessKeySecret, 112 | SecurityToken: *resp.SecurityToken, 113 | Expiration: *resp.Expiration, 114 | } 115 | return 116 | } 117 | 118 | func (provider *URLCredentialsProvider) needUpdateCredential() (result bool) { 119 | if provider.expirationTimestamp == 0 { 120 | return true 121 | } 122 | 123 | return provider.expirationTimestamp-time.Now().Unix() <= 180 124 | } 125 | 126 | func (provider *URLCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 127 | if provider.sessionCredentials == nil || provider.needUpdateCredential() { 128 | sessionCredentials, err1 := provider.getCredentials() 129 | if err1 != nil { 130 | return nil, err1 131 | } 132 | 133 | provider.sessionCredentials = sessionCredentials 134 | expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration) 135 | if err2 != nil { 136 | return nil, err2 137 | } 138 | provider.expirationTimestamp = expirationTime.Unix() 139 | } 140 | 141 | cc = &Credentials{ 142 | AccessKeyId: provider.sessionCredentials.AccessKeyId, 143 | AccessKeySecret: provider.sessionCredentials.AccessKeySecret, 144 | SecurityToken: provider.sessionCredentials.SecurityToken, 145 | ProviderName: provider.GetProviderName(), 146 | } 147 | return 148 | } 149 | 150 | func (provider *URLCredentialsProvider) GetProviderName() string { 151 | return "credential_uri" 152 | } 153 | -------------------------------------------------------------------------------- /credentials/providers/uri_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | httputil "github.com/aliyun/credentials-go/credentials/internal/http" 11 | "github.com/aliyun/credentials-go/credentials/internal/utils" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestNewURLCredentialsProvider(t *testing.T) { 16 | rollback := utils.Memory("ALIBABA_CLOUD_CREDENTIALS_URI") 17 | defer func() { 18 | rollback() 19 | }() 20 | // case 1: no credentials provider 21 | _, err := NewURLCredentialsProviderBuilder(). 22 | Build() 23 | assert.EqualError(t, err, "the url is empty") 24 | 25 | // case 2: no role arn 26 | os.Setenv("ALIBABA_CLOUD_CREDENTIALS_URI", "http://localhost:8080") 27 | p, err := NewURLCredentialsProviderBuilder(). 28 | Build() 29 | assert.Nil(t, err) 30 | assert.True(t, strings.HasPrefix(p.url, "http://localhost:8080")) 31 | 32 | // case 3: check default role session name 33 | p, err = NewURLCredentialsProviderBuilder(). 34 | WithUrl("http://localhost:9090"). 35 | Build() 36 | assert.Nil(t, err) 37 | assert.True(t, strings.HasPrefix(p.url, "http://localhost:9090")) 38 | } 39 | 40 | func TestURLCredentialsProvider_getCredentials(t *testing.T) { 41 | originHttpDo := httpDo 42 | defer func() { httpDo = originHttpDo }() 43 | p, err := NewURLCredentialsProviderBuilder(). 44 | WithUrl("http://localhost:8080"). 45 | Build() 46 | assert.Nil(t, err) 47 | 48 | // case 1: server error 49 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 50 | err = errors.New("mock server error") 51 | return 52 | } 53 | _, err = p.getCredentials() 54 | assert.NotNil(t, err) 55 | assert.Equal(t, "mock server error", err.Error()) 56 | 57 | // case 2: 4xx error 58 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 59 | res = &httputil.Response{ 60 | StatusCode: 400, 61 | Body: []byte("4xx error"), 62 | } 63 | return 64 | } 65 | 66 | _, err = p.getCredentials() 67 | assert.NotNil(t, err) 68 | assert.Equal(t, "get credentials from GET http://localhost:8080 failed: 4xx error", err.Error()) 69 | 70 | // case 3: invalid json 71 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 72 | res = &httputil.Response{ 73 | StatusCode: 200, 74 | Body: []byte("invalid json"), 75 | } 76 | return 77 | } 78 | _, err = p.getCredentials() 79 | assert.NotNil(t, err) 80 | assert.Equal(t, "get credentials from GET http://localhost:8080 failed with error, json unmarshal fail: invalid character 'i' looking for beginning of value", err.Error()) 81 | 82 | // case 4: empty response json 83 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 84 | res = &httputil.Response{ 85 | StatusCode: 200, 86 | Body: []byte("null"), 87 | } 88 | return 89 | } 90 | _, err = p.getCredentials() 91 | assert.NotNil(t, err) 92 | assert.Equal(t, "refresh credentials from GET http://localhost:8080 failed: null", err.Error()) 93 | 94 | // case 5: empty session ak response json 95 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 96 | res = &httputil.Response{ 97 | StatusCode: 200, 98 | Body: []byte(`{}`), 99 | } 100 | return 101 | } 102 | _, err = p.getCredentials() 103 | assert.NotNil(t, err) 104 | assert.Equal(t, "refresh credentials from GET http://localhost:8080 failed: {}", err.Error()) 105 | 106 | // case 6: mock ok value 107 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 108 | res = &httputil.Response{ 109 | StatusCode: 200, 110 | Body: []byte(`{"AccessKeyId":"saki","AccessKeySecret":"saks","Expiration":"2021-10-20T04:27:09Z","SecurityToken":"token"}`), 111 | } 112 | return 113 | } 114 | creds, err := p.getCredentials() 115 | assert.Nil(t, err) 116 | assert.Equal(t, "saki", creds.AccessKeyId) 117 | assert.Equal(t, "saks", creds.AccessKeySecret) 118 | assert.Equal(t, "token", creds.SecurityToken) 119 | assert.Equal(t, "2021-10-20T04:27:09Z", creds.Expiration) 120 | 121 | // needUpdateCredential 122 | assert.True(t, p.needUpdateCredential()) 123 | p.expirationTimestamp = time.Now().Unix() 124 | assert.True(t, p.needUpdateCredential()) 125 | 126 | p.expirationTimestamp = time.Now().Unix() + 300 127 | assert.False(t, p.needUpdateCredential()) 128 | } 129 | 130 | func TestURLCredentialsProvider_GetCredentials(t *testing.T) { 131 | originHttpDo := httpDo 132 | defer func() { httpDo = originHttpDo }() 133 | 134 | // case 0: get previous credentials failed 135 | p, err := NewURLCredentialsProviderBuilder(). 136 | WithUrl("http://localhost:8080"). 137 | Build() 138 | assert.Nil(t, err) 139 | 140 | // case 1: get credentials failed 141 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 142 | err = errors.New("mock server error") 143 | return 144 | } 145 | _, err = p.GetCredentials() 146 | assert.NotNil(t, err) 147 | assert.Equal(t, "mock server error", err.Error()) 148 | 149 | // case 2: get invalid expiration 150 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 151 | res = &httputil.Response{ 152 | StatusCode: 200, 153 | Body: []byte(`{"AccessKeyId":"akid","AccessKeySecret":"aksecret","Expiration":"invalidexpiration","SecurityToken":"ststoken"}`), 154 | } 155 | return 156 | } 157 | _, err = p.GetCredentials() 158 | assert.NotNil(t, err) 159 | assert.Equal(t, "parsing time \"invalidexpiration\" as \"2006-01-02T15:04:05Z\": cannot parse \"invalidexpiration\" as \"2006\"", err.Error()) 160 | 161 | // case 3: happy result 162 | httpDo = func(req *httputil.Request) (res *httputil.Response, err error) { 163 | res = &httputil.Response{ 164 | StatusCode: 200, 165 | Body: []byte(`{"AccessKeyId":"akid","AccessKeySecret":"aksecret","Expiration":"2021-10-20T04:27:09Z","SecurityToken":"ststoken"}`), 166 | } 167 | return 168 | } 169 | cc, err := p.GetCredentials() 170 | assert.Nil(t, err) 171 | assert.Equal(t, "akid", cc.AccessKeyId) 172 | assert.Equal(t, "aksecret", cc.AccessKeySecret) 173 | assert.Equal(t, "ststoken", cc.SecurityToken) 174 | assert.Equal(t, "credential_uri", cc.ProviderName) 175 | assert.True(t, p.needUpdateCredential()) 176 | // get credentials again 177 | cc, err = p.GetCredentials() 178 | assert.Nil(t, err) 179 | assert.Equal(t, "akid", cc.AccessKeyId) 180 | assert.Equal(t, "aksecret", cc.AccessKeySecret) 181 | assert.Equal(t, "ststoken", cc.SecurityToken) 182 | assert.Equal(t, "credential_uri", cc.ProviderName) 183 | assert.True(t, p.needUpdateCredential()) 184 | } 185 | 186 | func TestURLCredentialsProviderWithHttpOptions(t *testing.T) { 187 | p, err := NewURLCredentialsProviderBuilder(). 188 | WithUrl("http://localhost:8080"). 189 | WithHttpOptions(&HttpOptions{ 190 | ConnectTimeout: 1000, 191 | ReadTimeout: 1000, 192 | Proxy: "localhost:3999", 193 | }). 194 | Build() 195 | assert.Nil(t, err) 196 | _, err = p.GetCredentials() 197 | assert.NotNil(t, err) 198 | assert.Contains(t, err.Error(), "proxyconnect tcp:") 199 | } 200 | -------------------------------------------------------------------------------- /credentials/ram_role_arn_credentials_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/alibabacloud-go/tea/tea" 11 | "github.com/aliyun/credentials-go/credentials/internal/utils" 12 | "github.com/aliyun/credentials-go/credentials/request" 13 | ) 14 | 15 | const defaultDurationSeconds = 3600 16 | 17 | // RAMRoleArnCredentialsProvider is a kind of credentials 18 | type RAMRoleArnCredentialsProvider struct { 19 | *credentialUpdater 20 | AccessKeyId string 21 | AccessKeySecret string 22 | SecurityToken string 23 | RoleArn string 24 | RoleSessionName string 25 | RoleSessionExpiration int 26 | Policy string 27 | ExternalId string 28 | sessionCredential *sessionCredential 29 | runtime *utils.Runtime 30 | } 31 | 32 | type ramRoleArnResponse struct { 33 | Credentials *credentialsInResponse `json:"Credentials" xml:"Credentials"` 34 | } 35 | 36 | type credentialsInResponse struct { 37 | AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"` 38 | AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"` 39 | SecurityToken string `json:"SecurityToken" xml:"SecurityToken"` 40 | Expiration string `json:"Expiration" xml:"Expiration"` 41 | } 42 | 43 | func newRAMRoleArnl(accessKeyId, accessKeySecret, securityToken, roleArn, roleSessionName, policy string, roleSessionExpiration int, externalId string, runtime *utils.Runtime) *RAMRoleArnCredentialsProvider { 44 | return &RAMRoleArnCredentialsProvider{ 45 | AccessKeyId: accessKeyId, 46 | AccessKeySecret: accessKeySecret, 47 | SecurityToken: securityToken, 48 | RoleArn: roleArn, 49 | RoleSessionName: roleSessionName, 50 | RoleSessionExpiration: roleSessionExpiration, 51 | Policy: policy, 52 | ExternalId: externalId, 53 | credentialUpdater: new(credentialUpdater), 54 | runtime: runtime, 55 | } 56 | } 57 | 58 | func newRAMRoleArnCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string, roleSessionExpiration int, runtime *utils.Runtime) *RAMRoleArnCredentialsProvider { 59 | return &RAMRoleArnCredentialsProvider{ 60 | AccessKeyId: accessKeyId, 61 | AccessKeySecret: accessKeySecret, 62 | RoleArn: roleArn, 63 | RoleSessionName: roleSessionName, 64 | RoleSessionExpiration: roleSessionExpiration, 65 | Policy: policy, 66 | credentialUpdater: new(credentialUpdater), 67 | runtime: runtime, 68 | } 69 | } 70 | 71 | func newRAMRoleArnWithExternalIdCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy string, roleSessionExpiration int, externalId string, runtime *utils.Runtime) *RAMRoleArnCredentialsProvider { 72 | return &RAMRoleArnCredentialsProvider{ 73 | AccessKeyId: accessKeyId, 74 | AccessKeySecret: accessKeySecret, 75 | RoleArn: roleArn, 76 | RoleSessionName: roleSessionName, 77 | RoleSessionExpiration: roleSessionExpiration, 78 | Policy: policy, 79 | ExternalId: externalId, 80 | credentialUpdater: new(credentialUpdater), 81 | runtime: runtime, 82 | } 83 | } 84 | 85 | func (e *RAMRoleArnCredentialsProvider) GetCredential() (*CredentialModel, error) { 86 | if e.sessionCredential == nil || e.needUpdateCredential() { 87 | err := e.updateCredential() 88 | if err != nil { 89 | return nil, err 90 | } 91 | } 92 | credential := &CredentialModel{ 93 | AccessKeyId: tea.String(e.sessionCredential.AccessKeyId), 94 | AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret), 95 | SecurityToken: tea.String(e.sessionCredential.SecurityToken), 96 | Type: tea.String("ram_role_arn"), 97 | } 98 | return credential, nil 99 | } 100 | 101 | // GetAccessKeyId reutrns RAMRoleArnCredentialsProvider's AccessKeyId 102 | // if AccessKeyId is not exist or out of date, the function will update it. 103 | func (r *RAMRoleArnCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) { 104 | c, err := r.GetCredential() 105 | if err != nil { 106 | return 107 | } 108 | 109 | accessKeyId = c.AccessKeyId 110 | return 111 | } 112 | 113 | // GetAccessSecret reutrns RAMRoleArnCredentialsProvider's AccessKeySecret 114 | // if AccessKeySecret is not exist or out of date, the function will update it. 115 | func (r *RAMRoleArnCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) { 116 | c, err := r.GetCredential() 117 | if err != nil { 118 | return 119 | } 120 | 121 | accessKeySecret = c.AccessKeySecret 122 | return 123 | } 124 | 125 | // GetSecurityToken reutrns RAMRoleArnCredentialsProvider's SecurityToken 126 | // if SecurityToken is not exist or out of date, the function will update it. 127 | func (r *RAMRoleArnCredentialsProvider) GetSecurityToken() (securityToken *string, err error) { 128 | c, err := r.GetCredential() 129 | if err != nil { 130 | return 131 | } 132 | 133 | securityToken = c.SecurityToken 134 | return 135 | } 136 | 137 | // GetBearerToken is useless RAMRoleArnCredentialsProvider 138 | func (r *RAMRoleArnCredentialsProvider) GetBearerToken() *string { 139 | return tea.String("") 140 | } 141 | 142 | // GetType reutrns RAMRoleArnCredentialsProvider's type 143 | func (r *RAMRoleArnCredentialsProvider) GetType() *string { 144 | return tea.String("ram_role_arn") 145 | } 146 | 147 | func (r *RAMRoleArnCredentialsProvider) updateCredential() (err error) { 148 | if r.runtime == nil { 149 | r.runtime = new(utils.Runtime) 150 | } 151 | request := request.NewCommonRequest() 152 | request.Domain = "sts.aliyuncs.com" 153 | if r.runtime.STSEndpoint != "" { 154 | request.Domain = r.runtime.STSEndpoint 155 | } 156 | request.Scheme = "HTTPS" 157 | request.Method = "GET" 158 | request.QueryParams["AccessKeyId"] = r.AccessKeyId 159 | if r.SecurityToken != "" { 160 | request.QueryParams["SecurityToken"] = r.SecurityToken 161 | } 162 | request.QueryParams["Action"] = "AssumeRole" 163 | request.QueryParams["Format"] = "JSON" 164 | if r.RoleSessionExpiration > 0 { 165 | if r.RoleSessionExpiration >= 900 && r.RoleSessionExpiration <= 3600 { 166 | request.QueryParams["DurationSeconds"] = strconv.Itoa(r.RoleSessionExpiration) 167 | } else { 168 | err = errors.New("[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr") 169 | return 170 | } 171 | } else { 172 | request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds) 173 | } 174 | request.QueryParams["RoleArn"] = r.RoleArn 175 | if r.Policy != "" { 176 | request.QueryParams["Policy"] = r.Policy 177 | } 178 | if r.ExternalId != "" { 179 | request.QueryParams["ExternalId"] = r.ExternalId 180 | } 181 | request.QueryParams["RoleSessionName"] = r.RoleSessionName 182 | request.QueryParams["SignatureMethod"] = "HMAC-SHA1" 183 | request.QueryParams["SignatureVersion"] = "1.0" 184 | request.QueryParams["Version"] = "2015-04-01" 185 | request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601() 186 | request.QueryParams["SignatureNonce"] = utils.GetUUID() 187 | signature := utils.ShaHmac1(request.BuildStringToSign(), r.AccessKeySecret+"&") 188 | request.QueryParams["Signature"] = signature 189 | request.Headers["Host"] = request.Domain 190 | request.Headers["Accept-Encoding"] = "identity" 191 | request.URL = request.BuildURL() 192 | content, err := doAction(request, r.runtime) 193 | if err != nil { 194 | return fmt.Errorf("refresh RoleArn sts token err: %s", err.Error()) 195 | } 196 | var resp *ramRoleArnResponse 197 | err = json.Unmarshal(content, &resp) 198 | if err != nil { 199 | return fmt.Errorf("refresh RoleArn sts token err: Json.Unmarshal fail: %s", err.Error()) 200 | } 201 | if resp == nil || resp.Credentials == nil { 202 | return fmt.Errorf("refresh RoleArn sts token err: Credentials is empty") 203 | } 204 | respCredentials := resp.Credentials 205 | if respCredentials.AccessKeyId == "" || respCredentials.AccessKeySecret == "" || respCredentials.SecurityToken == "" || respCredentials.Expiration == "" { 206 | return fmt.Errorf("refresh RoleArn sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", respCredentials.AccessKeyId, respCredentials.AccessKeySecret, respCredentials.SecurityToken, respCredentials.Expiration) 207 | } 208 | 209 | expirationTime, err := time.Parse("2006-01-02T15:04:05Z", respCredentials.Expiration) 210 | r.lastUpdateTimestamp = time.Now().Unix() 211 | r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) 212 | r.sessionCredential = &sessionCredential{ 213 | AccessKeyId: respCredentials.AccessKeyId, 214 | AccessKeySecret: respCredentials.AccessKeySecret, 215 | SecurityToken: respCredentials.SecurityToken, 216 | } 217 | 218 | return 219 | } 220 | -------------------------------------------------------------------------------- /credentials/ram_role_arn_credentials_provider_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io/ioutil" 7 | "net/http" 8 | "strconv" 9 | "testing" 10 | 11 | "github.com/aliyun/credentials-go/credentials/internal/utils" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func mockResponse(statusCode int, content string, mockerr error) (res *http.Response, err error) { 16 | status := strconv.Itoa(statusCode) 17 | res = &http.Response{ 18 | Proto: "HTTP/1.1", 19 | ProtoMajor: 1, 20 | Header: map[string][]string{"sdk": {"test"}}, 21 | StatusCode: statusCode, 22 | Status: status + " " + http.StatusText(statusCode), 23 | } 24 | res.Body = ioutil.NopCloser(bytes.NewReader([]byte(content))) 25 | err = mockerr 26 | return 27 | } 28 | 29 | func Test_RoleArnCredential(t *testing.T) { 30 | auth := newRAMRoleArnCredential("accessKeyId", "accessKeySecret", "roleArn", "roleSessionName", "policy", 300, nil) 31 | origTestHookDo := hookDo 32 | defer func() { hookDo = origTestHookDo }() 33 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 34 | return func(req *http.Request) (*http.Response, error) { 35 | return mockResponse(200, `{"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration"}}`, errors.New("Internal error")) 36 | } 37 | } 38 | accesskeyId, err := auth.GetAccessKeyId() 39 | assert.NotNil(t, err) 40 | assert.Equal(t, "[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr", err.Error()) 41 | assert.Nil(t, accesskeyId) 42 | 43 | accesskeySecret, err := auth.GetAccessKeySecret() 44 | assert.NotNil(t, err) 45 | assert.Equal(t, "[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr", err.Error()) 46 | assert.Nil(t, accesskeySecret) 47 | 48 | ststoken, err := auth.GetSecurityToken() 49 | assert.NotNil(t, err) 50 | assert.Equal(t, "[InvalidParam]:Assume Role session duration should be in the range of 15min - 1Hr", err.Error()) 51 | assert.Nil(t, ststoken) 52 | 53 | assert.Equal(t, "", *auth.GetBearerToken()) 54 | assert.Equal(t, "ram_role_arn", *auth.GetType()) 55 | 56 | auth.RoleSessionExpiration = 1000 57 | accesskeyId, err = auth.GetAccessKeyId() 58 | assert.NotNil(t, err) 59 | assert.Equal(t, "refresh RoleArn sts token err: Internal error", err.Error()) 60 | assert.Nil(t, accesskeyId) 61 | 62 | auth.RoleSessionExpiration = 0 63 | accesskeyId, err = auth.GetAccessKeyId() 64 | assert.NotNil(t, err) 65 | assert.Equal(t, "refresh RoleArn sts token err: Internal error", err.Error()) 66 | assert.Nil(t, accesskeyId) 67 | 68 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 69 | return func(req *http.Request) (*http.Response, error) { 70 | return mockResponse(300, ``, nil) 71 | } 72 | } 73 | accesskeyId, err = auth.GetAccessKeyId() 74 | assert.NotNil(t, err) 75 | assert.Equal(t, "refresh RoleArn sts token err: httpStatus: 300, message = ", err.Error()) 76 | assert.Nil(t, accesskeyId) 77 | 78 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 79 | return func(req *http.Request) (*http.Response, error) { 80 | return mockResponse(200, `"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration"}}`, nil) 81 | } 82 | } 83 | accesskeyId, err = auth.GetAccessKeyId() 84 | assert.NotNil(t, err) 85 | assert.Equal(t, "refresh RoleArn sts token err: Json.Unmarshal fail: invalid character ':' after top-level value", err.Error()) 86 | assert.Nil(t, accesskeyId) 87 | 88 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 89 | return func(req *http.Request) (*http.Response, error) { 90 | return mockResponse(200, `{"Credentials":{"AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration"}}`, nil) 91 | } 92 | } 93 | accesskeyId, err = auth.GetAccessKeyId() 94 | assert.NotNil(t, err) 95 | assert.Equal(t, "refresh RoleArn sts token err: AccessKeyId: , AccessKeySecret: accessKeySecret, SecurityToken: securitytoken, Expiration: expiration", err.Error()) 96 | assert.Nil(t, accesskeyId) 97 | 98 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 99 | return func(req *http.Request) (*http.Response, error) { 100 | return mockResponse(200, `{}`, nil) 101 | } 102 | } 103 | accesskeyId, err = auth.GetAccessKeyId() 104 | assert.NotNil(t, err) 105 | assert.Equal(t, "refresh RoleArn sts token err: Credentials is empty", err.Error()) 106 | assert.Nil(t, accesskeyId) 107 | 108 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 109 | return func(req *http.Request) (*http.Response, error) { 110 | return mockResponse(200, `{"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"2020-01-02T15:04:05Z"}}`, nil) 111 | } 112 | } 113 | accesskeyId, err = auth.GetAccessKeyId() 114 | assert.Nil(t, err) 115 | assert.Equal(t, "accessKeyId", *accesskeyId) 116 | 117 | accesskeySecret, err = auth.GetAccessKeySecret() 118 | assert.Nil(t, err) 119 | assert.Equal(t, "accessKeySecret", *accesskeySecret) 120 | 121 | ststoken, err = auth.GetSecurityToken() 122 | assert.Nil(t, err) 123 | assert.Equal(t, "securitytoken", *ststoken) 124 | 125 | cred, err := auth.GetCredential() 126 | assert.Nil(t, err) 127 | assert.Equal(t, "accessKeyId", *cred.AccessKeyId) 128 | assert.Equal(t, "accessKeySecret", *cred.AccessKeySecret) 129 | assert.Equal(t, "securitytoken", *cred.SecurityToken) 130 | assert.Nil(t, cred.BearerToken) 131 | assert.Equal(t, "ram_role_arn", *cred.Type) 132 | 133 | auth = newRAMRoleArnCredential("accessKeyId", "accessKeySecret", "roleArn", "roleSessionName", "policy", 3600, &utils.Runtime{STSEndpoint: "www.aliyun.com"}) 134 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 135 | return func(req *http.Request) (*http.Response, error) { 136 | assert.Equal(t, "www.aliyun.com", req.Host) 137 | return mockResponse(200, `{}`, nil) 138 | } 139 | } 140 | accesskeyId, err = auth.GetAccessKeyId() 141 | assert.NotNil(t, err) 142 | assert.Equal(t, "refresh RoleArn sts token err: Credentials is empty", err.Error()) 143 | assert.Nil(t, accesskeyId) 144 | 145 | auth = newRAMRoleArnWithExternalIdCredential("accessKeyId", "accessKeySecret", "roleArn", "roleSessionName", "policy", 3600, "externalId", nil) 146 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 147 | return func(req *http.Request) (*http.Response, error) { 148 | return mockResponse(200, `{"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"2020-01-02T15:04:05Z"}}`, nil) 149 | } 150 | } 151 | accesskeyId, err = auth.GetAccessKeyId() 152 | assert.Nil(t, err) 153 | assert.Equal(t, "accessKeyId", *accesskeyId) 154 | 155 | accesskeySecret, err = auth.GetAccessKeySecret() 156 | assert.Nil(t, err) 157 | assert.Equal(t, "accessKeySecret", *accesskeySecret) 158 | 159 | ststoken, err = auth.GetSecurityToken() 160 | assert.Nil(t, err) 161 | assert.Equal(t, "securitytoken", *ststoken) 162 | } 163 | 164 | func TestStsRoleARNCredentialsProviderWithSecurityToken(t *testing.T) { 165 | auth := newRAMRoleArnl("accessKeyId", "accessKeySecret", "securityToken", "roleArn", "roleSessionName", "policy", 3600, "externalId", nil) 166 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 167 | return func(req *http.Request) (*http.Response, error) { 168 | assert.Equal(t, "securityToken", req.URL.Query().Get("SecurityToken")) 169 | return mockResponse(200, `{"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"2020-01-02T15:04:05Z"}}`, nil) 170 | } 171 | } 172 | 173 | _, err := auth.GetCredential() 174 | assert.Nil(t, err) 175 | } 176 | -------------------------------------------------------------------------------- /credentials/request/common_request.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | "time" 8 | 9 | "github.com/aliyun/credentials-go/credentials/utils" 10 | ) 11 | 12 | // CommonRequest is for requesting credential 13 | type CommonRequest struct { 14 | Scheme string 15 | Method string 16 | Domain string 17 | RegionId string 18 | URL string 19 | ReadTimeout time.Duration 20 | ConnectTimeout time.Duration 21 | isInsecure *bool 22 | BodyParams map[string]string 23 | userAgent map[string]string 24 | QueryParams map[string]string 25 | Headers map[string]string 26 | 27 | queries string 28 | } 29 | 30 | // NewCommonRequest returns a CommonRequest 31 | func NewCommonRequest() *CommonRequest { 32 | return &CommonRequest{ 33 | BodyParams: make(map[string]string), 34 | QueryParams: make(map[string]string), 35 | Headers: make(map[string]string), 36 | } 37 | } 38 | 39 | // BuildURL returns a url 40 | func (request *CommonRequest) BuildURL() string { 41 | url := fmt.Sprintf("%s://%s", strings.ToLower(request.Scheme), request.Domain) 42 | request.queries = "/?" + utils.GetURLFormedMap(request.QueryParams) 43 | return url + request.queries 44 | } 45 | 46 | // BuildStringToSign returns BuildStringToSign 47 | func (request *CommonRequest) BuildStringToSign() (stringToSign string) { 48 | signParams := make(map[string]string) 49 | for key, value := range request.QueryParams { 50 | signParams[key] = value 51 | } 52 | 53 | for key, value := range request.BodyParams { 54 | signParams[key] = value 55 | } 56 | stringToSign = utils.GetURLFormedMap(signParams) 57 | stringToSign = strings.Replace(stringToSign, "+", "%20", -1) 58 | stringToSign = strings.Replace(stringToSign, "*", "%2A", -1) 59 | stringToSign = strings.Replace(stringToSign, "%7E", "~", -1) 60 | stringToSign = url.QueryEscape(stringToSign) 61 | stringToSign = request.Method + "&%2F&" + stringToSign 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /credentials/request/common_request_test.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_BuildURL(t *testing.T) { 10 | r := NewCommonRequest() 11 | r.Domain = "domain.com" 12 | r.Scheme = "http" 13 | assert.Equal(t, "http://domain.com/?", r.BuildURL()) 14 | r.QueryParams["key"] = "value" 15 | assert.Equal(t, "http://domain.com/?key=value", r.BuildURL()) 16 | r.QueryParams["key"] = "https://domain/?q=v" 17 | assert.Equal(t, "http://domain.com/?key=https%3A%2F%2Fdomain%2F%3Fq%3Dv", r.BuildURL()) 18 | } 19 | 20 | func Test_BuildRpcStringToSign(t *testing.T) { 21 | request := NewCommonRequest() 22 | stringToSign := request.BuildStringToSign() 23 | assert.Equal(t, "&%2F&", stringToSign) 24 | request.QueryParams["q"] = "value" 25 | stringToSign = request.BuildStringToSign() 26 | assert.Equal(t, "&%2F&q%3Dvalue", stringToSign) 27 | request.QueryParams["q"] = "http://domain/?q=value&q2=value2" 28 | stringToSign = request.BuildStringToSign() 29 | assert.Equal(t, "&%2F&q%3Dhttp%253A%252F%252Fdomain%252F%253Fq%253Dvalue%2526q2%253Dvalue2", stringToSign) 30 | 31 | request.BodyParams["bq"] = "bq" 32 | stringToSign = request.BuildStringToSign() 33 | assert.Equal(t, "&%2F&bq%3Dbq%26q%3Dhttp%253A%252F%252Fdomain%252F%253Fq%253Dvalue%2526q2%253Dvalue2", stringToSign) 34 | } 35 | -------------------------------------------------------------------------------- /credentials/request/doc.go: -------------------------------------------------------------------------------- 1 | // Package request is used for internal. 2 | // You should not depend on it directly, breaking changes can and will be introducted to it. 3 | package request 4 | -------------------------------------------------------------------------------- /credentials/response/common_response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | var hookReadAll = func(fn func(r io.Reader) (b []byte, err error)) func(r io.Reader) (b []byte, err error) { 10 | return fn 11 | } 12 | 13 | // CommonResponse is for storing message of httpResponse 14 | type CommonResponse struct { 15 | httpStatus int 16 | httpHeaders map[string][]string 17 | httpContentString string 18 | httpContentBytes []byte 19 | } 20 | 21 | // ParseFromHTTPResponse assigns for CommonResponse, returns err when body is too large. 22 | func (resp *CommonResponse) ParseFromHTTPResponse(httpResponse *http.Response) (err error) { 23 | defer httpResponse.Body.Close() 24 | body, err := hookReadAll(ioutil.ReadAll)(httpResponse.Body) 25 | if err != nil { 26 | return 27 | } 28 | resp.httpStatus = httpResponse.StatusCode 29 | resp.httpHeaders = httpResponse.Header 30 | resp.httpContentBytes = body 31 | resp.httpContentString = string(body) 32 | return 33 | } 34 | 35 | // GetHTTPStatus returns httpStatus 36 | func (resp *CommonResponse) GetHTTPStatus() int { 37 | return resp.httpStatus 38 | } 39 | 40 | // GetHTTPHeaders returns httpresponse's headers 41 | func (resp *CommonResponse) GetHTTPHeaders() map[string][]string { 42 | return resp.httpHeaders 43 | } 44 | 45 | // GetHTTPContentString return body content as string 46 | func (resp *CommonResponse) GetHTTPContentString() string { 47 | return resp.httpContentString 48 | } 49 | 50 | // GetHTTPContentBytes return body content as []byte 51 | func (resp *CommonResponse) GetHTTPContentBytes() []byte { 52 | return resp.httpContentBytes 53 | } 54 | -------------------------------------------------------------------------------- /credentials/response/common_response_test.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func Test_ParseFromHTTPResponse(t *testing.T) { 16 | r := &CommonResponse{} 17 | res := &http.Response{ 18 | Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), 19 | StatusCode: 200, 20 | Header: make(map[string][]string), 21 | } 22 | res.Header.Add("Server", "GitHub.com") 23 | r.ParseFromHTTPResponse(res) 24 | assert.Equal(t, []byte{}, r.GetHTTPContentBytes()) 25 | assert.Equal(t, "", r.GetHTTPContentString()) 26 | assert.Equal(t, "GitHub.com", r.GetHTTPHeaders()["Server"][0]) 27 | assert.Equal(t, 200, r.GetHTTPStatus()) 28 | } 29 | 30 | func TestHookReadAll(t *testing.T) { 31 | fn := func(body io.Reader) (byt []byte, err error) { 32 | return nil, errors.New("hookReadAll") 33 | } 34 | result := hookReadAll(fn) 35 | byt, err := result(nil) 36 | assert.Nil(t, byt) 37 | assert.Equal(t, "hookReadAll", err.Error()) 38 | 39 | originHookReadAll := hookReadAll 40 | hookReadAll = func(old func(body io.Reader) (byt []byte, err error)) func(body io.Reader) (byt []byte, err error) { 41 | return fn 42 | } 43 | defer func() { 44 | hookReadAll = originHookReadAll 45 | }() 46 | commonResponse := &CommonResponse{} 47 | httpResponse := &http.Response{ 48 | Body: ioutil.NopCloser(strings.NewReader("creadential")), 49 | } 50 | err = commonResponse.ParseFromHTTPResponse(httpResponse) 51 | assert.Equal(t, "hookReadAll", err.Error()) 52 | } 53 | -------------------------------------------------------------------------------- /credentials/response/doc.go: -------------------------------------------------------------------------------- 1 | // Package request is used for internal. 2 | // You should not depend on it directly, breaking changes can and will be introducted to it. 3 | package response 4 | -------------------------------------------------------------------------------- /credentials/rsa_key_pair_credentials_provider.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/alibabacloud-go/tea/tea" 11 | "github.com/aliyun/credentials-go/credentials/internal/utils" 12 | "github.com/aliyun/credentials-go/credentials/request" 13 | ) 14 | 15 | // Deprecated: no more recommend to use it 16 | // RsaKeyPairCredentialsProvider is a kind of credentials provider 17 | type RsaKeyPairCredentialsProvider struct { 18 | *credentialUpdater 19 | PrivateKey string 20 | PublicKeyId string 21 | SessionExpiration int 22 | sessionCredential *sessionCredential 23 | runtime *utils.Runtime 24 | } 25 | 26 | type rsaKeyPairResponse struct { 27 | SessionAccessKey *sessionAccessKey `json:"SessionAccessKey" xml:"SessionAccessKey"` 28 | } 29 | 30 | type sessionAccessKey struct { 31 | SessionAccessKeyId string `json:"SessionAccessKeyId" xml:"SessionAccessKeyId"` 32 | SessionAccessKeySecret string `json:"SessionAccessKeySecret" xml:"SessionAccessKeySecret"` 33 | Expiration string `json:"Expiration" xml:"Expiration"` 34 | } 35 | 36 | func newRsaKeyPairCredential(privateKey, publicKeyId string, sessionExpiration int, runtime *utils.Runtime) *RsaKeyPairCredentialsProvider { 37 | return &RsaKeyPairCredentialsProvider{ 38 | PrivateKey: privateKey, 39 | PublicKeyId: publicKeyId, 40 | SessionExpiration: sessionExpiration, 41 | credentialUpdater: new(credentialUpdater), 42 | runtime: runtime, 43 | } 44 | } 45 | 46 | func (e *RsaKeyPairCredentialsProvider) GetCredential() (*CredentialModel, error) { 47 | if e.sessionCredential == nil || e.needUpdateCredential() { 48 | err := e.updateCredential() 49 | if err != nil { 50 | return nil, err 51 | } 52 | } 53 | credential := &CredentialModel{ 54 | AccessKeyId: tea.String(e.sessionCredential.AccessKeyId), 55 | AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret), 56 | SecurityToken: tea.String(e.sessionCredential.SecurityToken), 57 | Type: tea.String("rsa_key_pair"), 58 | } 59 | return credential, nil 60 | } 61 | 62 | // GetAccessKeyId reutrns RsaKeyPairCredential's AccessKeyId 63 | // if AccessKeyId is not exist or out of date, the function will update it. 64 | func (r *RsaKeyPairCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) { 65 | c, err := r.GetCredential() 66 | if err != nil { 67 | return 68 | } 69 | accessKeyId = c.AccessKeyId 70 | return 71 | } 72 | 73 | // GetAccessSecret reutrns RsaKeyPairCredential's AccessKeySecret 74 | // if AccessKeySecret is not exist or out of date, the function will update it. 75 | func (r *RsaKeyPairCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) { 76 | c, err := r.GetCredential() 77 | if err != nil { 78 | return 79 | } 80 | accessKeySecret = c.AccessKeySecret 81 | return 82 | } 83 | 84 | // GetSecurityToken is useless RsaKeyPairCredential 85 | func (r *RsaKeyPairCredentialsProvider) GetSecurityToken() (*string, error) { 86 | return tea.String(""), nil 87 | } 88 | 89 | // GetBearerToken is useless for RsaKeyPairCredential 90 | func (r *RsaKeyPairCredentialsProvider) GetBearerToken() *string { 91 | return tea.String("") 92 | } 93 | 94 | // GetType reutrns RsaKeyPairCredential's type 95 | func (r *RsaKeyPairCredentialsProvider) GetType() *string { 96 | return tea.String("rsa_key_pair") 97 | } 98 | 99 | func (r *RsaKeyPairCredentialsProvider) updateCredential() (err error) { 100 | if r.runtime == nil { 101 | r.runtime = new(utils.Runtime) 102 | } 103 | request := request.NewCommonRequest() 104 | request.Domain = "sts.aliyuncs.com" 105 | if r.runtime.Host != "" { 106 | request.Domain = r.runtime.Host 107 | } else if r.runtime.STSEndpoint != "" { 108 | request.Domain = r.runtime.STSEndpoint 109 | } 110 | request.Scheme = "HTTPS" 111 | request.Method = "GET" 112 | request.QueryParams["AccessKeyId"] = r.PublicKeyId 113 | request.QueryParams["Action"] = "GenerateSessionAccessKey" 114 | request.QueryParams["Format"] = "JSON" 115 | if r.SessionExpiration > 0 { 116 | if r.SessionExpiration >= 900 && r.SessionExpiration <= 3600 { 117 | request.QueryParams["DurationSeconds"] = strconv.Itoa(r.SessionExpiration) 118 | } else { 119 | err = errors.New("[InvalidParam]:Key Pair session duration should be in the range of 15min - 1Hr") 120 | return 121 | } 122 | } else { 123 | request.QueryParams["DurationSeconds"] = strconv.Itoa(defaultDurationSeconds) 124 | } 125 | request.QueryParams["SignatureMethod"] = "SHA256withRSA" 126 | request.QueryParams["SignatureType"] = "PRIVATEKEY" 127 | request.QueryParams["SignatureVersion"] = "1.0" 128 | request.QueryParams["Version"] = "2015-04-01" 129 | request.QueryParams["Timestamp"] = utils.GetTimeInFormatISO8601() 130 | request.QueryParams["SignatureNonce"] = utils.GetUUID() 131 | signature := utils.Sha256WithRsa(request.BuildStringToSign(), r.PrivateKey) 132 | request.QueryParams["Signature"] = signature 133 | request.Headers["Host"] = request.Domain 134 | request.Headers["Accept-Encoding"] = "identity" 135 | request.URL = request.BuildURL() 136 | content, err := doAction(request, r.runtime) 137 | if err != nil { 138 | return fmt.Errorf("refresh KeyPair err: %s", err.Error()) 139 | } 140 | var resp *rsaKeyPairResponse 141 | err = json.Unmarshal(content, &resp) 142 | if err != nil { 143 | return fmt.Errorf("refresh KeyPair err: Json Unmarshal fail: %s", err.Error()) 144 | } 145 | if resp == nil || resp.SessionAccessKey == nil { 146 | return fmt.Errorf("refresh KeyPair err: SessionAccessKey is empty") 147 | } 148 | sessionAccessKey := resp.SessionAccessKey 149 | if sessionAccessKey.SessionAccessKeyId == "" || sessionAccessKey.SessionAccessKeySecret == "" || sessionAccessKey.Expiration == "" { 150 | return fmt.Errorf("refresh KeyPair err: SessionAccessKeyId: %v, SessionAccessKeySecret: %v, Expiration: %v", sessionAccessKey.SessionAccessKeyId, sessionAccessKey.SessionAccessKeySecret, sessionAccessKey.Expiration) 151 | } 152 | 153 | expirationTime, err := time.Parse("2006-01-02T15:04:05Z", sessionAccessKey.Expiration) 154 | r.lastUpdateTimestamp = time.Now().Unix() 155 | r.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) 156 | r.sessionCredential = &sessionCredential{ 157 | AccessKeyId: sessionAccessKey.SessionAccessKeyId, 158 | AccessKeySecret: sessionAccessKey.SessionAccessKeySecret, 159 | } 160 | 161 | return 162 | } 163 | -------------------------------------------------------------------------------- /credentials/rsa_key_pair_credentials_provider_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/aliyun/credentials-go/credentials/internal/utils" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func Test_KeyPairCredential(t *testing.T) { 14 | privatekey := ` 15 | MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa 16 | 3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 17 | 3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ 18 | FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA 19 | kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I 20 | oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk 21 | Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq 22 | 1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu 23 | WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb 24 | WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN 25 | Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 26 | bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm 27 | 2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc 28 | vftlY0Hs1vNXcaBgEA==` 29 | auth := newRsaKeyPairCredential(privatekey, "publicKeyId", 100, &utils.Runtime{Host: "www.aliyun.com", Proxy: "www.aliyuncs.com"}) 30 | origTestHookDo := hookDo 31 | defer func() { hookDo = origTestHookDo }() 32 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 33 | return func(req *http.Request) (*http.Response, error) { 34 | return mockResponse(200, `{"Credentials":{"AccessKeyId":"accessKeyId","AccessKeySecret":"accessKeySecret","SecurityToken":"securitytoken","Expiration":"expiration"}}`, errors.New("Internal error")) 35 | } 36 | } 37 | accesskeyId, err := auth.GetAccessKeyId() 38 | assert.NotNil(t, err) 39 | assert.Equal(t, "[InvalidParam]:Key Pair session duration should be in the range of 15min - 1Hr", err.Error()) 40 | assert.Nil(t, accesskeyId) 41 | 42 | accesskeySecret, err := auth.GetAccessKeySecret() 43 | assert.NotNil(t, err) 44 | assert.Equal(t, "[InvalidParam]:Key Pair session duration should be in the range of 15min - 1Hr", err.Error()) 45 | assert.Nil(t, accesskeySecret) 46 | 47 | ststoken, err := auth.GetSecurityToken() 48 | assert.Nil(t, err) 49 | assert.Equal(t, "", *ststoken) 50 | 51 | assert.Equal(t, "", *auth.GetBearerToken()) 52 | assert.Equal(t, "rsa_key_pair", *auth.GetType()) 53 | 54 | auth.SessionExpiration = 1000 55 | accesskeyId, err = auth.GetAccessKeyId() 56 | assert.NotNil(t, err) 57 | assert.Equal(t, "refresh KeyPair err: Internal error", err.Error()) 58 | assert.Nil(t, accesskeyId) 59 | 60 | auth.SessionExpiration = 0 61 | accesskeyId, err = auth.GetAccessKeyId() 62 | assert.NotNil(t, err) 63 | assert.Equal(t, "refresh KeyPair err: Internal error", err.Error()) 64 | assert.Nil(t, accesskeyId) 65 | 66 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 67 | return func(req *http.Request) (*http.Response, error) { 68 | return mockResponse(300, ``, nil) 69 | } 70 | } 71 | accesskeyId, err = auth.GetAccessKeyId() 72 | assert.NotNil(t, err) 73 | assert.Equal(t, "refresh KeyPair err: httpStatus: 300, message = ", err.Error()) 74 | assert.Nil(t, accesskeyId) 75 | 76 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 77 | return func(req *http.Request) (*http.Response, error) { 78 | return mockResponse(200, `"SessionAccessKey":{"SessionAccessKeyId":"accessKeyId","SessionAccessKeySecret":"accessKeySecret","Expiration":"expiration"}}`, nil) 79 | } 80 | } 81 | accesskeyId, err = auth.GetAccessKeyId() 82 | assert.NotNil(t, err) 83 | assert.Equal(t, "refresh KeyPair err: Json Unmarshal fail: invalid character ':' after top-level value", err.Error()) 84 | assert.Nil(t, accesskeyId) 85 | 86 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 87 | return func(req *http.Request) (*http.Response, error) { 88 | return mockResponse(200, `{"SessionAccessKey":{"SessionAccessKeySecret":"accessKeySecret","Expiration":"expiration"}}`, nil) 89 | } 90 | } 91 | accesskeyId, err = auth.GetAccessKeyId() 92 | assert.NotNil(t, err) 93 | assert.Equal(t, "refresh KeyPair err: SessionAccessKeyId: , SessionAccessKeySecret: accessKeySecret, Expiration: expiration", err.Error()) 94 | assert.Nil(t, accesskeyId) 95 | 96 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 97 | return func(req *http.Request) (*http.Response, error) { 98 | return mockResponse(200, `{}`, nil) 99 | } 100 | } 101 | accesskeyId, err = auth.GetAccessKeyId() 102 | assert.NotNil(t, err) 103 | assert.Equal(t, "refresh KeyPair err: SessionAccessKey is empty", err.Error()) 104 | assert.Nil(t, accesskeyId) 105 | 106 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 107 | return func(req *http.Request) (*http.Response, error) { 108 | return mockResponse(200, `{"SessionAccessKey":{"SessionAccessKeyId":"accessKeyId","SessionAccessKeySecret":"accessKeySecret","Expiration":"2020-01-02T15:04:05Z"}}`, nil) 109 | } 110 | } 111 | accesskeyId, err = auth.GetAccessKeyId() 112 | assert.Nil(t, err) 113 | assert.Equal(t, "accessKeyId", *accesskeyId) 114 | 115 | accesskeySecret, err = auth.GetAccessKeySecret() 116 | assert.Nil(t, err) 117 | assert.Equal(t, "accessKeySecret", *accesskeySecret) 118 | 119 | cred, err := auth.GetCredential() 120 | assert.Nil(t, err) 121 | assert.Equal(t, "accessKeyId", *cred.AccessKeyId) 122 | assert.Equal(t, "accessKeySecret", *cred.AccessKeySecret) 123 | assert.Equal(t, "", *cred.SecurityToken) 124 | assert.Nil(t, cred.BearerToken) 125 | assert.Equal(t, "rsa_key_pair", *cred.Type) 126 | 127 | auth.runtime = nil 128 | auth.lastUpdateTimestamp = 0 129 | accesskeyId, err = auth.GetAccessKeyId() 130 | assert.Nil(t, err) 131 | assert.Equal(t, "accessKeyId", *accesskeyId) 132 | 133 | auth = newRsaKeyPairCredential(privatekey, "publicKeyId", 3600, &utils.Runtime{STSEndpoint: "www.aliyun.com"}) 134 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 135 | return func(req *http.Request) (*http.Response, error) { 136 | assert.Equal(t, "www.aliyun.com", req.Host) 137 | return mockResponse(200, `{}`, nil) 138 | } 139 | } 140 | accesskeyId, err = auth.GetAccessKeyId() 141 | assert.NotNil(t, err) 142 | assert.Equal(t, "refresh KeyPair err: SessionAccessKey is empty", err.Error()) 143 | assert.Nil(t, accesskeyId) 144 | } 145 | -------------------------------------------------------------------------------- /credentials/session_credential.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | type sessionCredential struct { 4 | AccessKeyId string 5 | AccessKeySecret string 6 | SecurityToken string 7 | } 8 | -------------------------------------------------------------------------------- /credentials/session_credential_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | -------------------------------------------------------------------------------- /credentials/uri_credential.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "time" 8 | 9 | "github.com/alibabacloud-go/tea/tea" 10 | "github.com/aliyun/credentials-go/credentials/internal/utils" 11 | "github.com/aliyun/credentials-go/credentials/request" 12 | ) 13 | 14 | // URLCredential is a kind of credential 15 | type URLCredentialsProvider struct { 16 | URL string 17 | *credentialUpdater 18 | *sessionCredential 19 | runtime *utils.Runtime 20 | } 21 | 22 | type URLResponse struct { 23 | AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"` 24 | AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"` 25 | SecurityToken string `json:"SecurityToken" xml:"SecurityToken"` 26 | Expiration string `json:"Expiration" xml:"Expiration"` 27 | } 28 | 29 | func newURLCredential(URL string) *URLCredentialsProvider { 30 | credentialUpdater := new(credentialUpdater) 31 | if URL == "" { 32 | URL = os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI") 33 | } 34 | return &URLCredentialsProvider{ 35 | URL: URL, 36 | credentialUpdater: credentialUpdater, 37 | } 38 | } 39 | 40 | func (e *URLCredentialsProvider) GetCredential() (*CredentialModel, error) { 41 | if e.sessionCredential == nil || e.needUpdateCredential() { 42 | err := e.updateCredential() 43 | if err != nil { 44 | return nil, err 45 | } 46 | } 47 | credential := &CredentialModel{ 48 | AccessKeyId: tea.String(e.sessionCredential.AccessKeyId), 49 | AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret), 50 | SecurityToken: tea.String(e.sessionCredential.SecurityToken), 51 | Type: tea.String("credential_uri"), 52 | } 53 | return credential, nil 54 | } 55 | 56 | // GetAccessKeyId reutrns URLCredential's AccessKeyId 57 | // if AccessKeyId is not exist or out of date, the function will update it. 58 | func (e *URLCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) { 59 | c, err := e.GetCredential() 60 | if err != nil { 61 | return 62 | } 63 | accessKeyId = c.AccessKeyId 64 | return 65 | } 66 | 67 | // GetAccessSecret reutrns URLCredential's AccessKeySecret 68 | // if AccessKeySecret is not exist or out of date, the function will update it. 69 | func (e *URLCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) { 70 | c, err := e.GetCredential() 71 | if err != nil { 72 | return 73 | } 74 | accessKeySecret = c.AccessKeySecret 75 | return 76 | } 77 | 78 | // GetSecurityToken reutrns URLCredential's SecurityToken 79 | // if SecurityToken is not exist or out of date, the function will update it. 80 | func (e *URLCredentialsProvider) GetSecurityToken() (securityToken *string, err error) { 81 | c, err := e.GetCredential() 82 | if err != nil { 83 | return 84 | } 85 | securityToken = c.SecurityToken 86 | return 87 | } 88 | 89 | // GetBearerToken is useless for URLCredential 90 | func (e *URLCredentialsProvider) GetBearerToken() *string { 91 | return tea.String("") 92 | } 93 | 94 | // GetType reutrns URLCredential's type 95 | func (e *URLCredentialsProvider) GetType() *string { 96 | return tea.String("credential_uri") 97 | } 98 | 99 | func (e *URLCredentialsProvider) updateCredential() (err error) { 100 | if e.runtime == nil { 101 | e.runtime = new(utils.Runtime) 102 | } 103 | request := request.NewCommonRequest() 104 | request.URL = e.URL 105 | request.Method = "GET" 106 | content, err := doAction(request, e.runtime) 107 | if err != nil { 108 | return fmt.Errorf("get credentials from %s failed with error: %s", e.URL, err.Error()) 109 | } 110 | var resp *URLResponse 111 | err = json.Unmarshal(content, &resp) 112 | if err != nil { 113 | return fmt.Errorf("get credentials from %s failed with error, json unmarshal fail: %s", e.URL, err.Error()) 114 | } 115 | if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" { 116 | return fmt.Errorf("get credentials failed: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration) 117 | } 118 | 119 | expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration) 120 | e.lastUpdateTimestamp = time.Now().Unix() 121 | e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) 122 | e.sessionCredential = &sessionCredential{ 123 | AccessKeyId: resp.AccessKeyId, 124 | AccessKeySecret: resp.AccessKeySecret, 125 | SecurityToken: resp.SecurityToken, 126 | } 127 | 128 | return 129 | } 130 | -------------------------------------------------------------------------------- /credentials/uri_credential_test.go: -------------------------------------------------------------------------------- 1 | package credentials 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestURLCredentialsProvider_updateCredential(t *testing.T) { 12 | provider := newURLCredential("http://127.0.0.1") 13 | 14 | origTestHookDo := hookDo 15 | defer func() { hookDo = origTestHookDo }() 16 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 17 | return func(req *http.Request) (*http.Response, error) { 18 | return mockResponse(300, ``, errors.New("sdk test")) 19 | } 20 | } 21 | 22 | cred, err := provider.GetCredential() 23 | assert.NotNil(t, err) 24 | assert.Equal(t, "get credentials from http://127.0.0.1 failed with error: sdk test", err.Error()) 25 | assert.Nil(t, cred) 26 | 27 | _, err = provider.GetAccessKeyId() 28 | assert.NotNil(t, err) 29 | assert.Equal(t, "get credentials from http://127.0.0.1 failed with error: sdk test", err.Error()) 30 | 31 | _, err = provider.GetAccessKeySecret() 32 | assert.NotNil(t, err) 33 | assert.Equal(t, "get credentials from http://127.0.0.1 failed with error: sdk test", err.Error()) 34 | 35 | _, err = provider.GetSecurityToken() 36 | assert.NotNil(t, err) 37 | assert.Equal(t, "get credentials from http://127.0.0.1 failed with error: sdk test", err.Error()) 38 | 39 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 40 | return func(req *http.Request) (*http.Response, error) { 41 | return mockResponse(200, `invalid json`, nil) 42 | } 43 | } 44 | 45 | cred, err = provider.GetCredential() 46 | assert.NotNil(t, err) 47 | assert.Equal(t, "get credentials from http://127.0.0.1 failed with error, json unmarshal fail: invalid character 'i' looking for beginning of value", err.Error()) 48 | assert.Nil(t, cred) 49 | 50 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 51 | return func(req *http.Request) (*http.Response, error) { 52 | return mockResponse(200, `{}`, nil) 53 | } 54 | } 55 | 56 | cred, err = provider.GetCredential() 57 | assert.NotNil(t, err) 58 | assert.Equal(t, "get credentials failed: AccessKeyId: , AccessKeySecret: , SecurityToken: , Expiration: ", err.Error()) 59 | assert.Nil(t, cred) 60 | 61 | hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { 62 | return func(req *http.Request) (*http.Response, error) { 63 | return mockResponse(200, `{"AccessKeyId":"akid", "AccessKeySecret":"aksecret","SecurityToken":"sts","Expiration":"2006-01-02T15:04:05Z"}`, nil) 64 | } 65 | } 66 | 67 | cred, err = provider.GetCredential() 68 | assert.Nil(t, err) 69 | assert.NotNil(t, cred) 70 | assert.Equal(t, "akid", *cred.AccessKeyId) 71 | assert.Equal(t, "aksecret", *cred.AccessKeySecret) 72 | assert.Equal(t, "sts", *cred.SecurityToken) 73 | 74 | akid, err := provider.GetAccessKeyId() 75 | assert.Nil(t, err) 76 | assert.Equal(t, "akid", *akid) 77 | 78 | aksecret, err := provider.GetAccessKeySecret() 79 | assert.Nil(t, err) 80 | assert.Equal(t, "aksecret", *aksecret) 81 | 82 | sts, err := provider.GetSecurityToken() 83 | assert.Nil(t, err) 84 | assert.Equal(t, "sts", *sts) 85 | } 86 | 87 | func TestURLCredentialsProviderGetBearerToken(t *testing.T) { 88 | provider := newURLCredential("http://127.0.0.1") 89 | assert.Equal(t, "", *provider.GetBearerToken()) 90 | } 91 | 92 | func TestURLCredentialsProviderGetType(t *testing.T) { 93 | provider := newURLCredential("http://127.0.0.1") 94 | assert.Equal(t, "credential_uri", *provider.GetType()) 95 | } 96 | -------------------------------------------------------------------------------- /credentials/utils/doc.go: -------------------------------------------------------------------------------- 1 | // Package request is used for internal. 2 | // You should not depend on it directly, breaking changes can and will be introducted to it. 3 | package utils 4 | -------------------------------------------------------------------------------- /credentials/utils/runtime.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // Runtime is for setting timeout, proxy and host 10 | // Deprecated: it was used for internal 11 | type Runtime struct { 12 | ReadTimeout int 13 | ConnectTimeout int 14 | Proxy string 15 | Host string 16 | STSEndpoint string 17 | } 18 | 19 | // NewRuntime returns a Runtime 20 | // Deprecated: it was used for internal 21 | func NewRuntime(readTimeout, connectTimeout int, proxy string, host string) *Runtime { 22 | return &Runtime{ 23 | ReadTimeout: readTimeout, 24 | ConnectTimeout: connectTimeout, 25 | Proxy: proxy, 26 | Host: host, 27 | } 28 | } 29 | 30 | // Timeout is for connect Timeout 31 | // Deprecated: it was used for internal 32 | func Timeout(connectTimeout time.Duration) func(cxt context.Context, net, addr string) (c net.Conn, err error) { 33 | return func(ctx context.Context, network, address string) (net.Conn, error) { 34 | return (&net.Dialer{ 35 | Timeout: connectTimeout, 36 | DualStack: true, 37 | }).DialContext(ctx, network, address) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /credentials/utils/runtime_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_NewRuntime(t *testing.T) { 12 | runitme := NewRuntime(10, 10, "proxy", "host") 13 | assert.Equal(t, 10, runitme.ReadTimeout) 14 | assert.Equal(t, 10, runitme.ConnectTimeout) 15 | assert.Equal(t, "proxy", runitme.Proxy) 16 | assert.Equal(t, "host", runitme.Host) 17 | 18 | dialContext := Timeout(5 * time.Second) 19 | ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second) 20 | assert.NotNil(t, cancelFunc) 21 | c, err := dialContext(ctx, "127.0.0.1", "127.0.0.2") 22 | assert.Nil(t, c) 23 | assert.Equal(t, "dial 127.0.0.1: unknown network 127.0.0.1", err.Error()) 24 | } 25 | -------------------------------------------------------------------------------- /credentials/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto" 5 | "crypto/hmac" 6 | "crypto/md5" 7 | "crypto/rand" 8 | "crypto/rsa" 9 | "crypto/sha1" 10 | "crypto/x509" 11 | "encoding/base64" 12 | "encoding/hex" 13 | "hash" 14 | "io" 15 | rand2 "math/rand" 16 | "net/url" 17 | "time" 18 | ) 19 | 20 | type uuid [16]byte 21 | 22 | const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 23 | 24 | var hookRead = func(fn func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { 25 | return fn 26 | } 27 | 28 | var hookRSA = func(fn func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 29 | return fn 30 | } 31 | 32 | // GetUUID returns a uuid 33 | // Deprecated: it was used for internal 34 | func GetUUID() (uuidHex string) { 35 | uuid := newUUID() 36 | uuidHex = hex.EncodeToString(uuid[:]) 37 | return 38 | } 39 | 40 | // RandStringBytes returns a rand string 41 | func RandStringBytes(n int) string { 42 | b := make([]byte, n) 43 | for i := range b { 44 | b[i] = letterBytes[rand2.Intn(len(letterBytes))] 45 | } 46 | return string(b) 47 | } 48 | 49 | // ShaHmac1 return a string which has been hashed 50 | // Deprecated: it was used for internal 51 | func ShaHmac1(source, secret string) string { 52 | key := []byte(secret) 53 | hmac := hmac.New(sha1.New, key) 54 | hmac.Write([]byte(source)) 55 | signedBytes := hmac.Sum(nil) 56 | signedString := base64.StdEncoding.EncodeToString(signedBytes) 57 | return signedString 58 | } 59 | 60 | // Sha256WithRsa return a string which has been hashed with Rsa 61 | // Deprecated: it was used for internal 62 | func Sha256WithRsa(source, secret string) string { 63 | decodeString, err := base64.StdEncoding.DecodeString(secret) 64 | if err != nil { 65 | panic(err) 66 | } 67 | private, err := x509.ParsePKCS8PrivateKey(decodeString) 68 | if err != nil { 69 | panic(err) 70 | } 71 | 72 | h := crypto.Hash.New(crypto.SHA256) 73 | h.Write([]byte(source)) 74 | hashed := h.Sum(nil) 75 | signature, err := hookRSA(rsa.SignPKCS1v15)(rand.Reader, private.(*rsa.PrivateKey), 76 | crypto.SHA256, hashed) 77 | if err != nil { 78 | panic(err) 79 | } 80 | 81 | return base64.StdEncoding.EncodeToString(signature) 82 | } 83 | 84 | // GetMD5Base64 returns a string which has been base64 85 | // Deprecated: it was used for internal 86 | func GetMD5Base64(bytes []byte) (base64Value string) { 87 | md5Ctx := md5.New() 88 | md5Ctx.Write(bytes) 89 | md5Value := md5Ctx.Sum(nil) 90 | base64Value = base64.StdEncoding.EncodeToString(md5Value) 91 | return 92 | } 93 | 94 | // GetTimeInFormatISO8601 returns a time string 95 | // Deprecated: it was used for internal 96 | func GetTimeInFormatISO8601() (timeStr string) { 97 | gmt := time.FixedZone("GMT", 0) 98 | 99 | return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") 100 | } 101 | 102 | // GetURLFormedMap returns a url encoded string 103 | // Deprecated: it was used for internal 104 | func GetURLFormedMap(source map[string]string) (urlEncoded string) { 105 | urlEncoder := url.Values{} 106 | for key, value := range source { 107 | urlEncoder.Add(key, value) 108 | } 109 | urlEncoded = urlEncoder.Encode() 110 | return 111 | } 112 | 113 | func newUUID() uuid { 114 | ns := uuid{} 115 | safeRandom(ns[:]) 116 | u := newFromHash(md5.New(), ns, RandStringBytes(16)) 117 | u[6] = (u[6] & 0x0f) | (byte(2) << 4) 118 | u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) 119 | 120 | return u 121 | } 122 | 123 | func newFromHash(h hash.Hash, ns uuid, name string) uuid { 124 | u := uuid{} 125 | h.Write(ns[:]) 126 | h.Write([]byte(name)) 127 | copy(u[:], h.Sum(nil)) 128 | 129 | return u 130 | } 131 | 132 | func safeRandom(dest []byte) { 133 | if _, err := hookRead(rand.Read)(dest); err != nil { 134 | panic(err) 135 | } 136 | } 137 | 138 | func (u uuid) String() string { 139 | buf := make([]byte, 36) 140 | 141 | hex.Encode(buf[0:8], u[0:4]) 142 | buf[8] = '-' 143 | hex.Encode(buf[9:13], u[4:6]) 144 | buf[13] = '-' 145 | hex.Encode(buf[14:18], u[6:8]) 146 | buf[18] = '-' 147 | hex.Encode(buf[19:23], u[8:10]) 148 | buf[23] = '-' 149 | hex.Encode(buf[24:], u[10:]) 150 | 151 | return string(buf) 152 | } 153 | -------------------------------------------------------------------------------- /credentials/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "errors" 7 | "io" 8 | "regexp" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestGetUUID(t *testing.T) { 15 | uuid := newUUID() 16 | assert.Equal(t, 16, len(uuid)) 17 | assert.Equal(t, 36, len(uuid.String())) 18 | uuidString := GetUUID() 19 | assert.Equal(t, 32, len(uuidString)) 20 | } 21 | 22 | func TestGetMD5Base64(t *testing.T) { 23 | assert.Equal(t, "ERIHLmRX2uZmssDdxQnnxQ==", 24 | GetMD5Base64([]byte("That's all folks!!"))) 25 | assert.Equal(t, "GsJRdI3kAbAnHo/0+3wWJw==", 26 | GetMD5Base64([]byte("中文也没啥问题"))) 27 | } 28 | 29 | func TestGetTimeInFormatISO8601(t *testing.T) { 30 | s := GetTimeInFormatISO8601() 31 | assert.Equal(t, 20, len(s)) 32 | // 2006-01-02T15:04:05Z 33 | re := regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$`) 34 | assert.True(t, re.MatchString(s)) 35 | } 36 | 37 | func TestGetURLFormedMap(t *testing.T) { 38 | m := make(map[string]string) 39 | m["key"] = "value" 40 | s := GetURLFormedMap(m) 41 | assert.Equal(t, "key=value", s) 42 | m["key2"] = "http://domain/?key=value&key2=value2" 43 | s2 := GetURLFormedMap(m) 44 | assert.Equal(t, "key=value&key2=http%3A%2F%2Fdomain%2F%3Fkey%3Dvalue%26key2%3Dvalue2", s2) 45 | } 46 | 47 | func TestShaHmac1(t *testing.T) { 48 | result := ShaHmac1("source", "secret") 49 | assert.Equal(t, "Jv4yi8SobFhg5t1C7nWLbhBSFZQ=", result) 50 | 51 | assert.Equal(t, "CqCYIa39h9SSWuXnTz8F5hh9UPA=", ShaHmac1("中文", "secret")) 52 | } 53 | 54 | func TestSha256WithRsa(t *testing.T) { 55 | secret := ` 56 | MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa 57 | 3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 58 | 3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ 59 | FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA 60 | kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I 61 | oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk 62 | Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq 63 | 1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu 64 | WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb 65 | WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN 66 | Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 67 | bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm 68 | 2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc 69 | vftlY0Hs1vNXcaBgEA==` 70 | result := Sha256WithRsa("source", secret) 71 | assert.Equal(t, "UNyJPD27jjSNl70b02E/DUtgtNESdtAuxbNBZTlksk1t/GYjiQNRlFIubp/EGKcWsqs7p5SFKnNiSRqWG3A51VmJFBXXtyW1nwLC9xY/MbUj6JVWNYCuLkPWM942O+GAk7N+G8ZQZt7ib2MhruDAUmv1lLN26lDaCPBX2MJQJCo=", result) 72 | 73 | assert.Equal(t, "CKE0osxUnFFH+oYP3Q427saucBuignE+Mrni63G9w46yZFtVoXFOu5lNiNCnUtaPNpGmBf9X5oGCY+otqPf7bP93nB59rfdteQs0sS65PWH9yjH8RwYCWGCbuyRul/0qIv/nYYGzkLON1C1Vx9Z4Yep6llYuJang5RIXrAkQLmQ=", Sha256WithRsa("中文", secret)) 74 | } 75 | 76 | func TestSha256WithRsa_DecodeString_Error(t *testing.T) { 77 | defer func() { // 进行异常捕捉 78 | err := recover() 79 | assert.NotNil(t, err) 80 | assert.Equal(t, "illegal base64 data at input byte 0", err.(error).Error()) 81 | }() 82 | secret := `==` 83 | Sha256WithRsa("source", secret) 84 | } 85 | 86 | func TestSha256WithRsa_ParsePKCS8PrivateKey_Error(t *testing.T) { 87 | defer func() { // 进行异常捕捉 88 | err := recover() 89 | assert.NotNil(t, err) 90 | assert.Equal(t, "asn1: structure error: length too large", err.(error).Error()) 91 | }() 92 | secret := `Jv4yi8SobFhg5t1C7nWLbhBSFZQ=` 93 | Sha256WithRsa("source", secret) 94 | } 95 | 96 | func TestHookRead(t *testing.T) { 97 | fn := func(p []byte) (n int, err error) { 98 | return 0, errors.New("hookRead") 99 | } 100 | result := hookRead(fn) 101 | n, err := result(nil) 102 | assert.Equal(t, 0, n) 103 | assert.Equal(t, "hookRead", err.Error()) 104 | 105 | originHookRead := hookRead 106 | hookRead = func(old func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { 107 | return fn 108 | } 109 | defer func() { 110 | err := recover() 111 | assert.Equal(t, "hookRead", err.(error).Error()) 112 | hookRead = originHookRead 113 | }() 114 | safeRandom([]byte("credentialtest")) 115 | } 116 | 117 | func TestHookRSA(t *testing.T) { 118 | fn := func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 119 | return nil, errors.New("hookRSA") 120 | } 121 | result := hookRSA(fn) 122 | hash := crypto.Hash(10) 123 | byt, err := result(nil, nil, hash, nil) 124 | assert.Nil(t, byt) 125 | assert.Equal(t, "hookRSA", err.Error()) 126 | 127 | originHookRSA := hookRSA 128 | hookRSA = func(old func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { 129 | return fn 130 | } 131 | defer func() { 132 | err := recover() 133 | assert.Equal(t, "hookRSA", err.(error).Error()) 134 | hookRSA = originHookRSA 135 | }() 136 | secret := ` 137 | MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa 138 | 3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 139 | 3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ 140 | FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA 141 | kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I 142 | oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk 143 | Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq 144 | 1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu 145 | WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb 146 | WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN 147 | Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 148 | bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm 149 | 2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc 150 | vftlY0Hs1vNXcaBgEA==` 151 | Sha256WithRsa("source", secret) 152 | } 153 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package credentials-go 2 | package credentials_go 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aliyun/credentials-go 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/alibabacloud-go/debug v1.0.1 7 | github.com/alibabacloud-go/tea v1.2.2 8 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 9 | github.com/stretchr/testify v1.5.1 10 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 11 | gopkg.in/ini.v1 v1.67.0 12 | gopkg.in/yaml.v2 v2.2.8 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= 2 | github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= 3 | github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= 4 | github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= 5 | github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 10 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 11 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 12 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 13 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 14 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 15 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 16 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 17 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 18 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 19 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 20 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 24 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 25 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 26 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 27 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 29 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 30 | golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= 31 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 32 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 33 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 34 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 35 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 36 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 37 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 38 | golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 39 | golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 40 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 41 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 47 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 48 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 50 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 51 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 52 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 53 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 54 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 55 | golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 56 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 57 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 58 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 59 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 60 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 61 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 62 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 63 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 64 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 65 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 66 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 67 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 68 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 69 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 70 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 71 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 72 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 73 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 74 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 75 | -------------------------------------------------------------------------------- /integration/auth_test.go: -------------------------------------------------------------------------------- 1 | package integeration 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/alibabacloud-go/tea/tea" 9 | "github.com/aliyun/credentials-go/credentials" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const ( 14 | EnvVarSubAccessKeyId = "SUB_ALICLOUD_ACCESS_KEY" 15 | EnvVarSubAccessKeySecret = "SUB_ALICLOUD_SECRET_KEY" 16 | EnvVarRoleArn = "ALICLOUD_ROLE_ARN" 17 | EnvVarRoleSessionName = "ALICLOUD_ROLE_SESSION_NAME" 18 | EnvVarRoleSessionExpiration = "ALICLOUD_ROLE_SESSION_EXPIRATION" 19 | ) 20 | 21 | func TestRAMRoleArn(t *testing.T) { 22 | rawexpiration := os.Getenv(EnvVarRoleSessionExpiration) 23 | expiration := 0 24 | if rawexpiration != "" { 25 | expiration, _ = strconv.Atoi(rawexpiration) 26 | } 27 | // assume role fisrt time 28 | config := &credentials.Config{ 29 | Type: tea.String("ram_role_arn"), 30 | AccessKeyId: tea.String(os.Getenv(EnvVarSubAccessKeyId)), 31 | AccessKeySecret: tea.String(os.Getenv(EnvVarSubAccessKeySecret)), 32 | RoleArn: tea.String(os.Getenv(EnvVarRoleArn)), 33 | RoleSessionName: tea.String(os.Getenv(EnvVarRoleSessionName)), 34 | RoleSessionExpiration: tea.Int(expiration), 35 | } 36 | cred, err := credentials.NewCredential(config) 37 | assert.Nil(t, err) 38 | assert.NotNil(t, cred) 39 | c, err := cred.GetCredential() 40 | assert.Nil(t, err) 41 | assert.NotNil(t, c.AccessKeyId) 42 | assert.NotNil(t, c.AccessKeySecret) 43 | assert.NotNil(t, c.SecurityToken) 44 | 45 | // asume role second time with pre sts 46 | config2 := &credentials.Config{ 47 | Type: tea.String("ram_role_arn"), 48 | AccessKeyId: c.AccessKeyId, 49 | AccessKeySecret: c.AccessKeySecret, 50 | SecurityToken: c.SecurityToken, 51 | RoleArn: tea.String(os.Getenv(EnvVarRoleArn)), 52 | RoleSessionName: tea.String(os.Getenv(EnvVarRoleSessionName)), 53 | RoleSessionExpiration: tea.Int(expiration), 54 | } 55 | cred2, err := credentials.NewCredential(config2) 56 | assert.Nil(t, err) 57 | assert.NotNil(t, cred2) 58 | c2, err := cred.GetCredential() 59 | assert.Nil(t, err) 60 | assert.NotNil(t, c2.AccessKeyId) 61 | assert.NotNil(t, c2.AccessKeySecret) 62 | assert.NotNil(t, c2.SecurityToken) 63 | } 64 | 65 | func TestOidc(t *testing.T) { 66 | config := &credentials.Config{ 67 | Type: tea.String("oidc_role_arn"), 68 | RoleArn: tea.String(os.Getenv("ALIBABA_CLOUD_ROLE_ARN")), 69 | OIDCProviderArn: tea.String(os.Getenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN")), 70 | OIDCTokenFilePath: tea.String(os.Getenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE")), 71 | RoleSessionName: tea.String("credentials-go-test"), 72 | } 73 | cred, err := credentials.NewCredential(config) 74 | assert.Nil(t, err) 75 | assert.NotNil(t, cred) 76 | c, err := cred.GetCredential() 77 | assert.Nil(t, err) 78 | assert.NotNil(t, c.AccessKeyId) 79 | assert.NotNil(t, c.AccessKeySecret) 80 | assert.NotNil(t, c.SecurityToken) 81 | assert.Equal(t, "oidc_role_arn", *c.Type) 82 | assert.Equal(t, "oidc_role_arn", *c.ProviderName) 83 | } 84 | 85 | func TestDefaultProvider(t *testing.T) { 86 | cred, err := credentials.NewCredential(nil) 87 | assert.Nil(t, err) 88 | assert.NotNil(t, cred) 89 | c, err := cred.GetCredential() 90 | assert.Nil(t, err) 91 | assert.NotNil(t, c.AccessKeyId) 92 | assert.NotNil(t, c.AccessKeySecret) 93 | assert.NotNil(t, c.SecurityToken) 94 | assert.Equal(t, "default", *c.Type) 95 | assert.Equal(t, "default/oidc_role_arn", *c.ProviderName) 96 | } 97 | -------------------------------------------------------------------------------- /integration/proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/alibabacloud-go/tea/tea" 8 | "github.com/aliyun/credentials-go/credentials" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestRAMRoleARNWithInvalidProxy(t *testing.T) { 13 | config := &credentials.Config{ 14 | Type: tea.String("ram_role_arn"), 15 | AccessKeyId: tea.String("akid"), 16 | AccessKeySecret: tea.String("aksecret"), 17 | RoleArn: tea.String("rolearn"), 18 | RoleSessionName: tea.String("rolesessionname"), 19 | RoleSessionExpiration: tea.Int(3600), 20 | Proxy: tea.String("https://localhost:3600/"), 21 | } 22 | cred, err := credentials.NewCredential(config) 23 | assert.Nil(t, err) 24 | _, err = cred.GetCredential() 25 | assert.Contains(t, err.Error(), "proxyconnect tcp: dial tcp") 26 | assert.Contains(t, err.Error(), ":3600: connect: connection refused") 27 | } 28 | 29 | func TestOIDCWithInvalidProxy(t *testing.T) { 30 | config := &credentials.Config{ 31 | Type: tea.String("oidc_role_arn"), 32 | RoleArn: tea.String(os.Getenv("ALIBABA_CLOUD_ROLE_ARN")), 33 | OIDCProviderArn: tea.String(os.Getenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN")), 34 | OIDCTokenFilePath: tea.String(os.Getenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE")), 35 | RoleSessionName: tea.String("credentials-go-test"), 36 | Proxy: tea.String("https://localhost:3600/"), 37 | } 38 | cred, err := credentials.NewCredential(config) 39 | assert.Nil(t, err) 40 | _, err = cred.GetCredential() 41 | assert.Contains(t, err.Error(), "proxyconnect tcp: dial tcp") 42 | assert.Contains(t, err.Error(), ":3600: connect: connection refused") 43 | } 44 | -------------------------------------------------------------------------------- /test_fixtures/empty_oidc_token: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/credentials-go/a528aa57113b84fa1b8d84f19d03258d5100bfb6/test_fixtures/empty_oidc_token -------------------------------------------------------------------------------- /test_fixtures/oidc_token: -------------------------------------------------------------------------------- 1 | test_long_oidc_token_eyJhbGciOiJSUzI1NiIsImtpZCI6ImFQaXlpNEVGSU8wWnlGcFh1V0psQUNWbklZVlJsUkNmM2tlSzNMUlhWT1UifQ.eyJhdWQiOlsic3RzLmFsaXl1bmNzLmNvbSJdLCJleHAiOjE2NDUxMTk3ODAsImlhdCI6MTY0NTA4Mzc4MCwiaXNzIjoiaHR0cHM6Ly9vaWRjLWFjay1jbi1oYW5nemhvdS5vc3MtY24taGFuZ3pob3UtaW50ZXJuYWwuYWxpeXVuY3MuY29tL2NmMWQ4ZGIwMjM0ZDk0YzEyOGFiZDM3MTc4NWJjOWQxNSIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoidGVzdC1ycnNhIiwicG9kIjp7Im5hbWUiOiJydW4tYXMtcm9vdCIsInVpZCI6ImIzMGI0MGY2LWNiZTAtNGY0Yy1hZGYyLWM1OGQ4ZmExZTAxMCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidXNlcjEiLCJ1aWQiOiJiZTEyMzdjYS01MTY4LTQyMzYtYWUyMC00NDM1YjhmMGI4YzAifX0sIm5iZiI6MTY0NTA4Mzc4MCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OnRlc3QtcnJzYTp1c2VyMSJ9.XGP-wgLj-iMiAHjLe0lZLh7y48Qsj9HzsEbNh706WwerBoxnssdsyGFb9lzd2FyM8CssbAOCstr7OuAMWNdJmDZgpiOGGSbQ-KXXmbfnIS4ix-V3pQF6LVBFr7xJlj20J6YY89um3rv_04t0iCGxKWs2ZMUyU1FbZpIPRep24LVKbUz1saiiVGgDBTIZdHA13Z-jUvYAnsxK_Kj5tc1K-IuQQU0IwSKJh5OShMcdPugMV5LwTL3ogCikfB7yljq5vclBhCeF2lXLIibvwF711TOhuJ5lMlh-a2KkIgwBHhANg_U9k4Mt_VadctfUGc4hxlSbBD0w9o9mDGKwgGmW5Q -------------------------------------------------------------------------------- /test_fixtures/pk.pem: -------------------------------------------------------------------------------- 1 | ---- 2 | this is privatekey --------------------------------------------------------------------------------