Skip to content

Commit

Permalink
Add default credentials provider(credentials provider chain)
Browse files Browse the repository at this point in the history
  • Loading branch information
JacksonTian committed Aug 16, 2024
1 parent bdee72c commit fbea940
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 44 deletions.
68 changes: 68 additions & 0 deletions sdk/auth/credentials/default_credentials_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package credentials

import (
"fmt"
"os"
"strings"
)

type DefaultCredentialsProvider struct {
providerChain []CredentialsProvider
lastUsedProvider CredentialsProvider
}

func NewDefaultCredentialsProvider() (provider *DefaultCredentialsProvider) {
providers := []CredentialsProvider{}

// Add static ak or sts credentials provider
providers = append(providers, NewEnvironmentVariableCredentialsProvider())

// oidc check
oidcProvider, err := NewOIDCCredentialsProviderBuilder().Build()
if err == nil {
providers = append(providers, oidcProvider)
}

// cli credentials provider
providers = append(providers, NewCLIProfileCredentialsProviderBuilder().Build())

// profile credentials provider
// providers = append(providers)
providers = append(providers, NewProfileCredentialsProviderBuilder().Build())

// Add IMDS
if os.Getenv("ALIBABA_CLOUD_ECS_METADATA") != "" {
ecsRamRoleProvider := NewECSRAMRoleCredentialsProvider(os.Getenv("ALIBABA_CLOUD_ECS_METADATA"))
providers = append(providers, ecsRamRoleProvider)
}

// TODO: ALIBABA_CLOUD_CREDENTIALS_URI check

return &DefaultCredentialsProvider{
providerChain: providers,
}
}

func (provider *DefaultCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
if provider.lastUsedProvider != nil {
return provider.lastUsedProvider.GetCredentials()
}

errors := []string{}
for _, p := range provider.providerChain {
provider.lastUsedProvider = p
cc, err = p.GetCredentials()
if err != nil {
errors = append(errors, err.Error())
// 如果有错误,进入下一个获取过程
continue
}

if cc != nil {
return
}
}

err = fmt.Errorf("unable to get credentials from any of the providers in the chain: %s", strings.Join(errors, ", "))
return
}
102 changes: 102 additions & 0 deletions sdk/auth/credentials/default_credentials_provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package credentials

import (
"os"
"testing"

"github.com/aliyun/alibaba-cloud-sdk-go/sdk/internal"
"github.com/stretchr/testify/assert"
)

func TestDefaultCredentialsProvider(t *testing.T) {
provider := NewDefaultCredentialsProvider()
assert.NotNil(t, provider)
assert.Len(t, provider.providerChain, 3)
_, ok := provider.providerChain[0].(*EnvironmentVariableCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[1].(*CLIProfileCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[2].(*ProfileCredentialsProvider)
assert.True(t, ok)

// Add oidc provider
rollback := internal.Memory("ALIBABA_CLOUD_OIDC_TOKEN_FILE",
"ALIBABA_CLOUD_OIDC_PROVIDER_ARN",
"ALIBABA_CLOUD_ROLE_ARN",
"ALIBABA_CLOUD_ECS_METADATA")

defer rollback()
os.Setenv("ALIBABA_CLOUD_OIDC_TOKEN_FILE", "/path/to/oidc.token")
os.Setenv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN", "oidcproviderarn")
os.Setenv("ALIBABA_CLOUD_ROLE_ARN", "rolearn")

provider = NewDefaultCredentialsProvider()
assert.NotNil(t, provider)
assert.Len(t, provider.providerChain, 4)
_, ok = provider.providerChain[0].(*EnvironmentVariableCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[1].(*OIDCCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[2].(*CLIProfileCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[3].(*ProfileCredentialsProvider)
assert.True(t, ok)

// Add ecs ram role
os.Setenv("ALIBABA_CLOUD_ECS_METADATA", "rolename")
provider = NewDefaultCredentialsProvider()
assert.NotNil(t, provider)
assert.Len(t, provider.providerChain, 5)
_, ok = provider.providerChain[0].(*EnvironmentVariableCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[1].(*OIDCCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[2].(*CLIProfileCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[3].(*ProfileCredentialsProvider)
assert.True(t, ok)

_, ok = provider.providerChain[4].(*ECSRAMRoleCredentialsProvider)
assert.True(t, ok)
}

func TestDefaultCredentialsProvider_GetCredentials(t *testing.T) {
rollback := internal.Memory("ALIBABA_CLOUD_ACCESS_KEY_ID",
"ALIBABA_CLOUD_ACCESS_KEY_SECRET",
"ALIBABA_CLOUD_SECURITY_TOKEN")

defer func() {
getHomePath = internal.GetHomePath
rollback()
}()

// testcase: empty home
getHomePath = func() string {
return ""
}

provider := NewDefaultCredentialsProvider()
assert.Len(t, provider.providerChain, 3)
_, err := provider.GetCredentials()
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")

os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_ID", "akid")
os.Setenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "aksecret")
provider = NewDefaultCredentialsProvider()
assert.Len(t, provider.providerChain, 3)
cc, err := provider.GetCredentials()
assert.Nil(t, err)
assert.Equal(t, &Credentials{AccessKeyId: "akid", AccessKeySecret: "aksecret", SecurityToken: "", BearerToken: ""}, cc)
// get again
cc, err = provider.GetCredentials()
assert.Nil(t, err)
assert.Equal(t, &Credentials{AccessKeyId: "akid", AccessKeySecret: "aksecret", SecurityToken: "", BearerToken: ""}, cc)
}
2 changes: 1 addition & 1 deletion sdk/auth/credentials/profile_credentials_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (b *ProfileCredentialsProviderBuilder) WithProfileName(profileName string)
return b
}

func (b *ProfileCredentialsProviderBuilder) Build() (provider *ProfileCredentialsProvider, err error) {
func (b *ProfileCredentialsProviderBuilder) Build() (provider *ProfileCredentialsProvider) {
// 优先级:
// 1. 使用显示指定的 profileName
// 2. 使用环境变量(ALIBABA_CLOUD_PROFILE)指定的 profileName
Expand Down
64 changes: 21 additions & 43 deletions sdk/auth/credentials/profile_credentials_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,24 @@ func TestProfileCredentialsProviderBuilder(t *testing.T) {
defer rollback()

// profile name from specified
provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)
provider := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Equal(t, "custom", provider.profileName)

// profile name from env
os.Setenv("ALIBABA_CLOUD_PROFILE", "profile_from_env")
provider, err = NewProfileCredentialsProviderBuilder().Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().Build()

assert.Equal(t, "profile_from_env", provider.profileName)

// profile name from default
os.Setenv("ALIBABA_CLOUD_PROFILE", "")
provider, err = NewProfileCredentialsProviderBuilder().Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().Build()
assert.Equal(t, "default", provider.profileName)
}

func TestProfileCredentialsProvider_getCredentialsProvider(t *testing.T) {
provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)

_, err = provider.getCredentialsProvider(ini.Empty())
provider := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
_, err := provider.getCredentialsProvider(ini.Empty())
assert.NotNil(t, err)
assert.EqualError(t, err, "ERROR: Can not load sectionsection \"custom\" does not exist")

Expand All @@ -122,29 +117,25 @@ func TestProfileCredentialsProvider_getCredentialsProvider(t *testing.T) {
assert.NotNil(t, file)

// no type
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("notype").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("notype").Build()
_, err = provider.getCredentialsProvider(file)
assert.NotNil(t, err)
assert.EqualError(t, err, "ERROR: Can not find credential typeerror when getting key of section \"notype\": key \"type\" not exists")

// no ak
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noak").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("noak").Build()
_, err = provider.getCredentialsProvider(file)
assert.NotNil(t, err)
assert.EqualError(t, err, "ERROR: Failed to get value")

// value is empty
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("emptyak").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("emptyak").Build()
_, err = provider.getCredentialsProvider(file)
assert.NotNil(t, err)
assert.EqualError(t, err, "ERROR: Value can't be empty")

// static ak provider
provider, err = NewProfileCredentialsProviderBuilder().Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().Build()
cp, err := provider.getCredentialsProvider(file)
assert.Nil(t, err)
akcp, ok := cp.(*StaticAKCredentialsProvider)
Expand All @@ -154,42 +145,36 @@ func TestProfileCredentialsProvider_getCredentialsProvider(t *testing.T) {
assert.Equal(t, &Credentials{AccessKeyId: "foo", AccessKeySecret: "bar", SecurityToken: "", BearerToken: ""}, cc)

// ecs_ram_role without rolename
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noecs").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("noecs").Build()
_, err = provider.getCredentialsProvider(file)
assert.EqualError(t, err, "ERROR: Failed to get value")

// ecs_ram_role with rolename
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("ecs").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("ecs").Build()
cp, err = provider.getCredentialsProvider(file)
assert.Nil(t, err)
_, ok = cp.(*ECSRAMRoleCredentialsProvider)
assert.True(t, ok)

// ram role arn without keys
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("noram").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("noram").Build()
_, err = provider.getCredentialsProvider(file)
assert.EqualError(t, err, "ERROR: Failed to get value")

// ram role arn without values
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("emptyram").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("emptyram").Build()
_, err = provider.getCredentialsProvider(file)
assert.EqualError(t, err, "ERROR: Value can't be empty")

// normal ram role arn
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("ram").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("ram").Build()
cp, err = provider.getCredentialsProvider(file)
assert.Nil(t, err)
_, ok = cp.(*RAMRoleARNCredentialsProvider)
assert.True(t, ok)

// unsupported type
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("error_type").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("error_type").Build()
_, err = provider.getCredentialsProvider(file)
assert.EqualError(t, err, "ERROR: Failed to get credential")
}
Expand All @@ -205,25 +190,22 @@ func TestProfileCredentialsProviderGetCredentials(t *testing.T) {
getHomePath = func() string {
return ""
}
provider, err := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)
_, err = provider.GetCredentials()
provider := NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
_, err := provider.GetCredentials()
assert.EqualError(t, err, "cannot found home dir")

// testcase: invalid home
getHomePath = func() string {
return "/path/invalid/home/dir"
}

provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
_, err = provider.GetCredentials()
assert.EqualError(t, err, "ERROR: Can not open fileopen /path/invalid/home/dir/.alibabacloud/credentials: no such file or directory")

// testcase: specify credentials file with env
os.Setenv("ALIBABA_CLOUD_CREDENTIALS_FILE", "/path/to/credentials.invalid")
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
_, err = provider.GetCredentials()
assert.EqualError(t, err, "ERROR: Can not open fileopen /path/to/credentials.invalid: no such file or directory")
os.Unsetenv("ALIBABA_CLOUD_CREDENTIALS_FILE")
Expand All @@ -234,15 +216,11 @@ func TestProfileCredentialsProviderGetCredentials(t *testing.T) {
return path.Join(wd, "fixtures")
}

//
provider, err = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().WithProfileName("custom").Build()
_, err = provider.GetCredentials()
assert.EqualError(t, err, "ERROR: Can not load sectionsection \"custom\" does not exist")

//
provider, err = NewProfileCredentialsProviderBuilder().Build()
assert.Nil(t, err)
provider = NewProfileCredentialsProviderBuilder().Build()
cc, err := provider.GetCredentials()
assert.Nil(t, err)
assert.Equal(t, &Credentials{AccessKeyId: "foo", AccessKeySecret: "bar", SecurityToken: "", BearerToken: ""}, cc)
Expand Down

0 comments on commit fbea940

Please sign in to comment.