profile_provider.go raw

   1  package credentials
   2  
   3  import (
   4  	"errors"
   5  	"fmt"
   6  	"os"
   7  	"strings"
   8  
   9  	"github.com/alibabacloud-go/tea/tea"
  10  	"github.com/aliyun/credentials-go/credentials/internal/utils"
  11  	ini "gopkg.in/ini.v1"
  12  )
  13  
  14  type profileProvider struct {
  15  	Profile string
  16  }
  17  
  18  var providerProfile = newProfileProvider()
  19  
  20  var hookState = func(info os.FileInfo, err error) (os.FileInfo, error) {
  21  	return info, err
  22  }
  23  
  24  // NewProfileProvider receive zero or more parameters,
  25  // when length of name is 0, the value of field Profile will be "default",
  26  // and when there are multiple inputs, the function will take the
  27  // first one and  discard the other values.
  28  func newProfileProvider(name ...string) Provider {
  29  	p := new(profileProvider)
  30  	if len(name) == 0 {
  31  		p.Profile = "default"
  32  	} else {
  33  		p.Profile = name[0]
  34  	}
  35  	return p
  36  }
  37  
  38  // resolve implements the Provider interface
  39  // when credential type is rsa_key_pair, the content of private_key file
  40  // must be able to be parsed directly into the required string
  41  // that NewRsaKeyPairCredential function needed
  42  func (p *profileProvider) resolve() (*Config, error) {
  43  	path, ok := os.LookupEnv(ENVCredentialFile)
  44  	if !ok {
  45  		defaultPath, err := checkDefaultPath()
  46  		if err != nil {
  47  			return nil, err
  48  		}
  49  		path = defaultPath
  50  		if path == "" {
  51  			return nil, nil
  52  		}
  53  	} else if path == "" {
  54  		return nil, errors.New(ENVCredentialFile + " cannot be empty")
  55  	}
  56  
  57  	value, section, err := getType(path, p.Profile)
  58  	if err != nil {
  59  		return nil, err
  60  	}
  61  	switch value.String() {
  62  	case "access_key":
  63  		config, err := getAccessKey(section)
  64  		if err != nil {
  65  			return nil, err
  66  		}
  67  		return config, nil
  68  	case "sts":
  69  		config, err := getSTS(section)
  70  		if err != nil {
  71  			return nil, err
  72  		}
  73  		return config, nil
  74  	case "bearer":
  75  		config, err := getBearerToken(section)
  76  		if err != nil {
  77  			return nil, err
  78  		}
  79  		return config, nil
  80  	case "ecs_ram_role":
  81  		config, err := getEcsRAMRole(section)
  82  		if err != nil {
  83  			return nil, err
  84  		}
  85  		return config, nil
  86  	case "ram_role_arn":
  87  		config, err := getRAMRoleArn(section)
  88  		if err != nil {
  89  			return nil, err
  90  		}
  91  		return config, nil
  92  	case "rsa_key_pair":
  93  		config, err := getRSAKeyPair(section)
  94  		if err != nil {
  95  			return nil, err
  96  		}
  97  		return config, nil
  98  	default:
  99  		return nil, errors.New("invalid type option, support: access_key, sts, ecs_ram_role, ram_role_arn, rsa_key_pair")
 100  	}
 101  }
 102  
 103  func getRSAKeyPair(section *ini.Section) (*Config, error) {
 104  	publicKeyId, err := section.GetKey("public_key_id")
 105  	if err != nil {
 106  		return nil, errors.New("missing required public_key_id option in profile for rsa_key_pair")
 107  	}
 108  	if publicKeyId.String() == "" {
 109  		return nil, errors.New("public_key_id cannot be empty")
 110  	}
 111  	privateKeyFile, err := section.GetKey("private_key_file")
 112  	if err != nil {
 113  		return nil, errors.New("missing required private_key_file option in profile for rsa_key_pair")
 114  	}
 115  	if privateKeyFile.String() == "" {
 116  		return nil, errors.New("private_key_file cannot be empty")
 117  	}
 118  	sessionExpiration, _ := section.GetKey("session_expiration")
 119  	expiration := 0
 120  	if sessionExpiration != nil {
 121  		expiration, err = sessionExpiration.Int()
 122  		if err != nil {
 123  			return nil, errors.New("session_expiration must be an int")
 124  		}
 125  	}
 126  	config := &Config{
 127  		Type:              tea.String("rsa_key_pair"),
 128  		PublicKeyId:       tea.String(publicKeyId.String()),
 129  		PrivateKeyFile:    tea.String(privateKeyFile.String()),
 130  		SessionExpiration: tea.Int(expiration),
 131  	}
 132  	err = setRuntimeToConfig(config, section)
 133  	if err != nil {
 134  		return nil, err
 135  	}
 136  	return config, nil
 137  }
 138  
 139  func getRAMRoleArn(section *ini.Section) (*Config, error) {
 140  	accessKeyId, err := section.GetKey("access_key_id")
 141  	if err != nil {
 142  		return nil, errors.New("missing required access_key_id option in profile for ram_role_arn")
 143  	}
 144  	if accessKeyId.String() == "" {
 145  		return nil, errors.New("access_key_id cannot be empty")
 146  	}
 147  	accessKeySecret, err := section.GetKey("access_key_secret")
 148  	if err != nil {
 149  		return nil, errors.New("missing required access_key_secret option in profile for ram_role_arn")
 150  	}
 151  	if accessKeySecret.String() == "" {
 152  		return nil, errors.New("access_key_secret cannot be empty")
 153  	}
 154  	roleArn, err := section.GetKey("role_arn")
 155  	if err != nil {
 156  		return nil, errors.New("missing required role_arn option in profile for ram_role_arn")
 157  	}
 158  	if roleArn.String() == "" {
 159  		return nil, errors.New("role_arn cannot be empty")
 160  	}
 161  	roleSessionName, err := section.GetKey("role_session_name")
 162  	if err != nil {
 163  		return nil, errors.New("missing required role_session_name option in profile for ram_role_arn")
 164  	}
 165  	if roleSessionName.String() == "" {
 166  		return nil, errors.New("role_session_name cannot be empty")
 167  	}
 168  	roleSessionExpiration, _ := section.GetKey("role_session_expiration")
 169  	expiration := 0
 170  	if roleSessionExpiration != nil {
 171  		expiration, err = roleSessionExpiration.Int()
 172  		if err != nil {
 173  			return nil, errors.New("role_session_expiration must be an int")
 174  		}
 175  	}
 176  	config := &Config{
 177  		Type:                  tea.String("ram_role_arn"),
 178  		AccessKeyId:           tea.String(accessKeyId.String()),
 179  		AccessKeySecret:       tea.String(accessKeySecret.String()),
 180  		RoleArn:               tea.String(roleArn.String()),
 181  		RoleSessionName:       tea.String(roleSessionName.String()),
 182  		RoleSessionExpiration: tea.Int(expiration),
 183  	}
 184  	err = setRuntimeToConfig(config, section)
 185  	if err != nil {
 186  		return nil, err
 187  	}
 188  	return config, nil
 189  }
 190  
 191  func getEcsRAMRole(section *ini.Section) (*Config, error) {
 192  	roleName, _ := section.GetKey("role_name")
 193  	config := &Config{
 194  		Type: tea.String("ecs_ram_role"),
 195  	}
 196  	if roleName != nil {
 197  		config.RoleName = tea.String(roleName.String())
 198  	}
 199  	err := setRuntimeToConfig(config, section)
 200  	if err != nil {
 201  		return nil, err
 202  	}
 203  	return config, nil
 204  }
 205  
 206  func getBearerToken(section *ini.Section) (*Config, error) {
 207  	bearerToken, err := section.GetKey("bearer_token")
 208  	if err != nil {
 209  		return nil, errors.New("missing required bearer_token option in profile for bearer")
 210  	}
 211  	if bearerToken.String() == "" {
 212  		return nil, errors.New("bearer_token cannot be empty")
 213  	}
 214  	config := &Config{
 215  		Type:        tea.String("bearer"),
 216  		BearerToken: tea.String(bearerToken.String()),
 217  	}
 218  	return config, nil
 219  }
 220  
 221  func getSTS(section *ini.Section) (*Config, error) {
 222  	accesskeyid, err := section.GetKey("access_key_id")
 223  	if err != nil {
 224  		return nil, errors.New("missing required access_key_id option in profile for sts")
 225  	}
 226  	if accesskeyid.String() == "" {
 227  		return nil, errors.New("access_key_id cannot be empty")
 228  	}
 229  	accessKeySecret, err := section.GetKey("access_key_secret")
 230  	if err != nil {
 231  		return nil, errors.New("missing required access_key_secret option in profile for sts")
 232  	}
 233  	if accessKeySecret.String() == "" {
 234  		return nil, errors.New("access_key_secret cannot be empty")
 235  	}
 236  	securityToken, err := section.GetKey("security_token")
 237  	if err != nil {
 238  		return nil, errors.New("missing required security_token option in profile for sts")
 239  	}
 240  	if securityToken.String() == "" {
 241  		return nil, errors.New("security_token cannot be empty")
 242  	}
 243  	config := &Config{
 244  		Type:            tea.String("sts"),
 245  		AccessKeyId:     tea.String(accesskeyid.String()),
 246  		AccessKeySecret: tea.String(accessKeySecret.String()),
 247  		SecurityToken:   tea.String(securityToken.String()),
 248  	}
 249  	return config, nil
 250  }
 251  
 252  func getAccessKey(section *ini.Section) (*Config, error) {
 253  	accesskeyid, err := section.GetKey("access_key_id")
 254  	if err != nil {
 255  		return nil, errors.New("missing required access_key_id option in profile for access_key")
 256  	}
 257  	if accesskeyid.String() == "" {
 258  		return nil, errors.New("access_key_id cannot be empty")
 259  	}
 260  	accessKeySecret, err := section.GetKey("access_key_secret")
 261  	if err != nil {
 262  		return nil, errors.New("missing required access_key_secret option in profile for access_key")
 263  	}
 264  	if accessKeySecret.String() == "" {
 265  		return nil, errors.New("access_key_secret cannot be empty")
 266  	}
 267  	config := &Config{
 268  		Type:            tea.String("access_key"),
 269  		AccessKeyId:     tea.String(accesskeyid.String()),
 270  		AccessKeySecret: tea.String(accessKeySecret.String()),
 271  	}
 272  	return config, nil
 273  }
 274  
 275  func getType(path, profile string) (*ini.Key, *ini.Section, error) {
 276  	ini, err := ini.Load(path)
 277  	if err != nil {
 278  		return nil, nil, errors.New("ERROR: Can not open file " + err.Error())
 279  	}
 280  
 281  	section, err := ini.GetSection(profile)
 282  	if err != nil {
 283  		return nil, nil, errors.New("ERROR: Can not load section " + err.Error())
 284  	}
 285  
 286  	value, err := section.GetKey("type")
 287  	if err != nil {
 288  		return nil, nil, errors.New("missing required type option " + err.Error())
 289  	}
 290  	return value, section, nil
 291  }
 292  
 293  func checkDefaultPath() (path string, err error) {
 294  	path = utils.GetHomePath()
 295  	if path == "" {
 296  		return "", errors.New("the default credential file path is invalid")
 297  	}
 298  	path = strings.Replace("~/.alibabacloud/credentials", "~", path, 1)
 299  	_, err = hookState(os.Stat(path))
 300  	if err != nil {
 301  		return "", nil
 302  	}
 303  	return path, nil
 304  }
 305  
 306  func setRuntimeToConfig(config *Config, section *ini.Section) error {
 307  	rawTimeout, _ := section.GetKey("timeout")
 308  	rawConnectTimeout, _ := section.GetKey("connect_timeout")
 309  	rawProxy, _ := section.GetKey("proxy")
 310  	rawHost, _ := section.GetKey("host")
 311  	if rawProxy != nil {
 312  		config.Proxy = tea.String(rawProxy.String())
 313  	}
 314  	if rawConnectTimeout != nil {
 315  		connectTimeout, err := rawConnectTimeout.Int()
 316  		if err != nil {
 317  			return fmt.Errorf("please set connect_timeout with an int value")
 318  		}
 319  		config.ConnectTimeout = tea.Int(connectTimeout)
 320  	}
 321  	if rawTimeout != nil {
 322  		timeout, err := rawTimeout.Int()
 323  		if err != nil {
 324  			return fmt.Errorf("please set timeout with an int value")
 325  		}
 326  		config.Timeout = tea.Int(timeout)
 327  	}
 328  	if rawHost != nil {
 329  		config.Host = tea.String(rawHost.String())
 330  	}
 331  	return nil
 332  }
 333