profile_provider.go 9.6 KB

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