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