package credentials import ( "errors" "fmt" "os" "runtime" "strings" "github.com/alibabacloud-go/tea/tea" ini "gopkg.in/ini.v1" ) type profileProvider struct { Profile string } var providerProfile = newProfileProvider() var hookOS = func(goos string) string { return goos } var hookState = func(info os.FileInfo, err error) (os.FileInfo, error) { return info, err } // NewProfileProvider receive zero or more parameters, // when length of name is 0, the value of field Profile will be "default", // and when there are multiple inputs, the function will take the // first one and discard the other values. func newProfileProvider(name ...string) Provider { p := new(profileProvider) if len(name) == 0 { p.Profile = "default" } else { p.Profile = name[0] } return p } // resolve implements the Provider interface // when credential type is rsa_key_pair, the content of private_key file // must be able to be parsed directly into the required string // that NewRsaKeyPairCredential function needed func (p *profileProvider) resolve() (*Config, error) { path, ok := os.LookupEnv(ENVCredentialFile) if !ok { defaultPath, err := checkDefaultPath() if err != nil { return nil, err } path = defaultPath if path == "" { return nil, nil } } else if path == "" { return nil, errors.New(ENVCredentialFile + " cannot be empty") } value, section, err := getType(path, p.Profile) if err != nil { return nil, err } switch value.String() { case "access_key": config, err := getAccessKey(section) if err != nil { return nil, err } return config, nil case "sts": config, err := getSTS(section) if err != nil { return nil, err } return config, nil case "bearer": config, err := getBearerToken(section) if err != nil { return nil, err } return config, nil case "ecs_ram_role": config, err := getEcsRAMRole(section) if err != nil { return nil, err } return config, nil case "ram_role_arn": config, err := getRAMRoleArn(section) if err != nil { return nil, err } return config, nil case "rsa_key_pair": config, err := getRSAKeyPair(section) if err != nil { return nil, err } return config, nil default: return nil, errors.New("Invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair") } } func getRSAKeyPair(section *ini.Section) (*Config, error) { publicKeyId, err := section.GetKey("public_key_id") if err != nil { return nil, errors.New("Missing required public_key_id option in profile for rsa_key_pair") } if publicKeyId.String() == "" { return nil, errors.New("public_key_id cannot be empty") } privateKeyFile, err := section.GetKey("private_key_file") if err != nil { return nil, errors.New("Missing required private_key_file option in profile for rsa_key_pair") } if privateKeyFile.String() == "" { return nil, errors.New("private_key_file cannot be empty") } sessionExpiration, _ := section.GetKey("session_expiration") expiration := 0 if sessionExpiration != nil { expiration, err = sessionExpiration.Int() if err != nil { return nil, errors.New("session_expiration must be an int") } } config := &Config{ Type: tea.String("rsa_key_pair"), PublicKeyId: tea.String(publicKeyId.String()), PrivateKeyFile: tea.String(privateKeyFile.String()), SessionExpiration: tea.Int(expiration), } err = setRuntimeToConfig(config, section) if err != nil { return nil, err } return config, nil } func getRAMRoleArn(section *ini.Section) (*Config, error) { accessKeyId, err := section.GetKey("access_key_id") if err != nil { return nil, errors.New("Missing required access_key_id option in profile for ram_role_arn") } if accessKeyId.String() == "" { return nil, errors.New("access_key_id cannot be empty") } accessKeySecret, err := section.GetKey("access_key_secret") if err != nil { return nil, errors.New("Missing required access_key_secret option in profile for ram_role_arn") } if accessKeySecret.String() == "" { return nil, errors.New("access_key_secret cannot be empty") } roleArn, err := section.GetKey("role_arn") if err != nil { return nil, errors.New("Missing required role_arn option in profile for ram_role_arn") } if roleArn.String() == "" { return nil, errors.New("role_arn cannot be empty") } roleSessionName, err := section.GetKey("role_session_name") if err != nil { return nil, errors.New("Missing required role_session_name option in profile for ram_role_arn") } if roleSessionName.String() == "" { return nil, errors.New("role_session_name cannot be empty") } roleSessionExpiration, _ := section.GetKey("role_session_expiration") expiration := 0 if roleSessionExpiration != nil { expiration, err = roleSessionExpiration.Int() if err != nil { return nil, errors.New("role_session_expiration must be an int") } } config := &Config{ Type: tea.String("ram_role_arn"), AccessKeyId: tea.String(accessKeyId.String()), AccessKeySecret: tea.String(accessKeySecret.String()), RoleArn: tea.String(roleArn.String()), RoleSessionName: tea.String(roleSessionName.String()), RoleSessionExpiration: tea.Int(expiration), } err = setRuntimeToConfig(config, section) if err != nil { return nil, err } return config, nil } func getEcsRAMRole(section *ini.Section) (*Config, error) { roleName, _ := section.GetKey("role_name") config := &Config{ Type: tea.String("ecs_ram_role"), } if roleName != nil { config.RoleName = tea.String(roleName.String()) } err := setRuntimeToConfig(config, section) if err != nil { return nil, err } return config, nil } func getBearerToken(section *ini.Section) (*Config, error) { bearerToken, err := section.GetKey("bearer_token") if err != nil { return nil, errors.New("Missing required bearer_token option in profile for bearer") } if bearerToken.String() == "" { return nil, errors.New("bearer_token cannot be empty") } config := &Config{ Type: tea.String("bearer"), BearerToken: tea.String(bearerToken.String()), } return config, nil } func getSTS(section *ini.Section) (*Config, error) { accesskeyid, err := section.GetKey("access_key_id") if err != nil { return nil, errors.New("Missing required access_key_id option in profile for sts") } if accesskeyid.String() == "" { return nil, errors.New("access_key_id cannot be empty") } accessKeySecret, err := section.GetKey("access_key_secret") if err != nil { return nil, errors.New("Missing required access_key_secret option in profile for sts") } if accessKeySecret.String() == "" { return nil, errors.New("access_key_secret cannot be empty") } securityToken, err := section.GetKey("security_token") if err != nil { return nil, errors.New("Missing required security_token option in profile for sts") } if securityToken.String() == "" { return nil, errors.New("security_token cannot be empty") } config := &Config{ Type: tea.String("sts"), AccessKeyId: tea.String(accesskeyid.String()), AccessKeySecret: tea.String(accessKeySecret.String()), SecurityToken: tea.String(securityToken.String()), } return config, nil } func getAccessKey(section *ini.Section) (*Config, error) { accesskeyid, err := section.GetKey("access_key_id") if err != nil { return nil, errors.New("Missing required access_key_id option in profile for access_key") } if accesskeyid.String() == "" { return nil, errors.New("access_key_id cannot be empty") } accessKeySecret, err := section.GetKey("access_key_secret") if err != nil { return nil, errors.New("Missing required access_key_secret option in profile for access_key") } if accessKeySecret.String() == "" { return nil, errors.New("access_key_secret cannot be empty") } config := &Config{ Type: tea.String("access_key"), AccessKeyId: tea.String(accesskeyid.String()), AccessKeySecret: tea.String(accessKeySecret.String()), } return config, nil } func getType(path, profile string) (*ini.Key, *ini.Section, error) { ini, err := ini.Load(path) if err != nil { return nil, nil, errors.New("ERROR: Can not open file " + err.Error()) } section, err := ini.GetSection(profile) if err != nil { return nil, nil, errors.New("ERROR: Can not load section " + err.Error()) } value, err := section.GetKey("type") if err != nil { return nil, nil, errors.New("Missing required type option " + err.Error()) } return value, section, nil } func getHomePath() string { if hookOS(runtime.GOOS) == "windows" { path, ok := os.LookupEnv("USERPROFILE") if !ok { return "" } return path } path, ok := os.LookupEnv("HOME") if !ok { return "" } return path } func checkDefaultPath() (path string, err error) { path = getHomePath() if path == "" { return "", errors.New("The default credential file path is invalid") } path = strings.Replace("~/.alibabacloud/credentials", "~", path, 1) _, err = hookState(os.Stat(path)) if err != nil { return "", nil } return path, nil } func setRuntimeToConfig(config *Config, section *ini.Section) error { rawTimeout, _ := section.GetKey("timeout") rawConnectTimeout, _ := section.GetKey("connect_timeout") rawProxy, _ := section.GetKey("proxy") rawHost, _ := section.GetKey("host") if rawProxy != nil { config.Proxy = tea.String(rawProxy.String()) } if rawConnectTimeout != nil { connectTimeout, err := rawConnectTimeout.Int() if err != nil { return fmt.Errorf("Please set connect_timeout with an int value") } config.ConnectTimeout = tea.Int(connectTimeout) } if rawTimeout != nil { timeout, err := rawTimeout.Int() if err != nil { return fmt.Errorf("Please set timeout with an int value") } config.Timeout = tea.Int(timeout) } if rawHost != nil { config.Host = tea.String(rawHost.String()) } return nil }