351 lines
9.6 KiB
Go
351 lines
9.6 KiB
Go
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 {
|
|
path, err := checkDefaultPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|