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