github.com/aliyun/credentials-go@v1.4.7/credentials/providers/cli_profile.go (about)

     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  	SignInUrl         string `json:"cloud_sso_sign_in_url"`
    85  	AccountId         string `json:"cloud_sso_account_id"`
    86  	AccessConfig      string `json:"cloud_sso_access_config"`
    87  	AccessToken       string `json:"access_token"`
    88  	AccessTokenExpire int64  `json:"cloud_sso_access_token_expire"`
    89  }
    90  
    91  type configuration struct {
    92  	Current  string     `json:"current"`
    93  	Profiles []*profile `json:"profiles"`
    94  }
    95  
    96  func newConfigurationFromPath(cfgPath string) (conf *configuration, err error) {
    97  	bytes, err := ioutil.ReadFile(cfgPath)
    98  	if err != nil {
    99  		err = fmt.Errorf("reading aliyun cli config from '%s' failed %v", cfgPath, err)
   100  		return
   101  	}
   102  
   103  	conf = &configuration{}
   104  
   105  	err = json.Unmarshal(bytes, conf)
   106  	if err != nil {
   107  		err = fmt.Errorf("unmarshal aliyun cli config from '%s' failed: %s", cfgPath, string(bytes))
   108  		return
   109  	}
   110  
   111  	if conf.Profiles == nil || len(conf.Profiles) == 0 {
   112  		err = fmt.Errorf("no any configured profiles in '%s'", cfgPath)
   113  		return
   114  	}
   115  
   116  	return
   117  }
   118  
   119  func (conf *configuration) getProfile(name string) (profile *profile, err error) {
   120  	for _, p := range conf.Profiles {
   121  		if p.Name == name {
   122  			profile = p
   123  			return
   124  		}
   125  	}
   126  
   127  	err = fmt.Errorf("unable to get profile with '%s'", name)
   128  	return
   129  }
   130  
   131  func (provider *CLIProfileCredentialsProvider) getCredentialsProvider(conf *configuration, profileName string) (credentialsProvider CredentialsProvider, err error) {
   132  	p, err := conf.getProfile(profileName)
   133  	if err != nil {
   134  		return
   135  	}
   136  
   137  	switch p.Mode {
   138  	case "AK":
   139  		credentialsProvider, err = NewStaticAKCredentialsProviderBuilder().
   140  			WithAccessKeyId(p.AccessKeyID).
   141  			WithAccessKeySecret(p.AccessKeySecret).
   142  			Build()
   143  	case "StsToken":
   144  		credentialsProvider, err = NewStaticSTSCredentialsProviderBuilder().
   145  			WithAccessKeyId(p.AccessKeyID).
   146  			WithAccessKeySecret(p.AccessKeySecret).
   147  			WithSecurityToken(p.SecurityToken).
   148  			Build()
   149  	case "RamRoleArn":
   150  		previousProvider, err1 := NewStaticAKCredentialsProviderBuilder().
   151  			WithAccessKeyId(p.AccessKeyID).
   152  			WithAccessKeySecret(p.AccessKeySecret).
   153  			Build()
   154  		if err1 != nil {
   155  			return nil, err1
   156  		}
   157  
   158  		credentialsProvider, err = NewRAMRoleARNCredentialsProviderBuilder().
   159  			WithCredentialsProvider(previousProvider).
   160  			WithRoleArn(p.RoleArn).
   161  			WithRoleSessionName(p.RoleSessionName).
   162  			WithDurationSeconds(p.DurationSeconds).
   163  			WithStsRegionId(p.StsRegion).
   164  			WithEnableVpc(p.EnableVpc).
   165  			WithPolicy(p.Policy).
   166  			WithExternalId(p.ExternalId).
   167  			Build()
   168  	case "EcsRamRole":
   169  		credentialsProvider, err = NewECSRAMRoleCredentialsProviderBuilder().WithRoleName(p.RoleName).Build()
   170  	case "OIDC":
   171  		credentialsProvider, err = NewOIDCCredentialsProviderBuilder().
   172  			WithOIDCTokenFilePath(p.OIDCTokenFile).
   173  			WithOIDCProviderARN(p.OIDCProviderARN).
   174  			WithRoleArn(p.RoleArn).
   175  			WithStsRegionId(p.StsRegion).
   176  			WithEnableVpc(p.EnableVpc).
   177  			WithDurationSeconds(p.DurationSeconds).
   178  			WithRoleSessionName(p.RoleSessionName).
   179  			WithPolicy(p.Policy).
   180  			Build()
   181  	case "ChainableRamRoleArn":
   182  		previousProvider, err1 := provider.getCredentialsProvider(conf, p.SourceProfile)
   183  		if err1 != nil {
   184  			err = fmt.Errorf("get source profile failed: %s", err1.Error())
   185  			return
   186  		}
   187  		credentialsProvider, err = NewRAMRoleARNCredentialsProviderBuilder().
   188  			WithCredentialsProvider(previousProvider).
   189  			WithRoleArn(p.RoleArn).
   190  			WithRoleSessionName(p.RoleSessionName).
   191  			WithDurationSeconds(p.DurationSeconds).
   192  			WithStsRegionId(p.StsRegion).
   193  			WithEnableVpc(p.EnableVpc).
   194  			WithPolicy(p.Policy).
   195  			WithExternalId(p.ExternalId).
   196  			Build()
   197  	case "CloudSSO":
   198  		credentialsProvider, err = NewCloudSSOCredentialsProviderBuilder().
   199  			WithSignInUrl(p.SignInUrl).
   200  			WithAccountId(p.AccountId).
   201  			WithAccessConfig(p.AccessConfig).
   202  			WithAccessToken(p.AccessToken).
   203  			WithAccessTokenExpire(p.AccessTokenExpire).
   204  			Build()
   205  	default:
   206  		err = fmt.Errorf("unsupported profile mode '%s'", p.Mode)
   207  	}
   208  
   209  	return
   210  }
   211  
   212  // 默认设置为 GetHomePath,测试时便于 mock
   213  var getHomePath = utils.GetHomePath
   214  
   215  func (provider *CLIProfileCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
   216  	if provider.innerProvider == nil {
   217  		cfgPath := provider.profileFile
   218  		if cfgPath == "" {
   219  			homeDir := getHomePath()
   220  			if homeDir == "" {
   221  				err = fmt.Errorf("cannot found home dir")
   222  				return
   223  			}
   224  
   225  			cfgPath = path.Join(homeDir, ".aliyun/config.json")
   226  		}
   227  
   228  		conf, err1 := newConfigurationFromPath(cfgPath)
   229  		if err1 != nil {
   230  			err = err1
   231  			return
   232  		}
   233  
   234  		if provider.profileName == "" {
   235  			provider.profileName = conf.Current
   236  		}
   237  
   238  		provider.innerProvider, err = provider.getCredentialsProvider(conf, provider.profileName)
   239  		if err != nil {
   240  			return
   241  		}
   242  	}
   243  
   244  	innerCC, err := provider.innerProvider.GetCredentials()
   245  	if err != nil {
   246  		return
   247  	}
   248  
   249  	providerName := innerCC.ProviderName
   250  	if providerName == "" {
   251  		providerName = provider.innerProvider.GetProviderName()
   252  	}
   253  
   254  	cc = &Credentials{
   255  		AccessKeyId:     innerCC.AccessKeyId,
   256  		AccessKeySecret: innerCC.AccessKeySecret,
   257  		SecurityToken:   innerCC.SecurityToken,
   258  		ProviderName:    fmt.Sprintf("%s/%s", provider.GetProviderName(), providerName),
   259  	}
   260  
   261  	return
   262  }
   263  
   264  func (provider *CLIProfileCredentialsProvider) GetProviderName() string {
   265  	return "cli_profile"
   266  }