configuration.go raw
1 // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2 // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
3
4 package common
5
6 import (
7 "crypto/rsa"
8 "errors"
9 "fmt"
10 "io/ioutil"
11 "os"
12 "path/filepath"
13 "regexp"
14 "strings"
15 "sync"
16 )
17
18 // AuthenticationType for auth
19 type AuthenticationType string
20
21 const (
22 // UserPrincipal is default auth type
23 UserPrincipal AuthenticationType = "user_principal"
24 // InstancePrincipal is used for instance principal auth type
25 InstancePrincipal AuthenticationType = "instance_principal"
26 // InstancePrincipalDelegationToken is used for instance principal delegation token auth type
27 InstancePrincipalDelegationToken AuthenticationType = "instance_principle_delegation_token"
28 // ResourcePrincipalDelegationToken is used for resource principal delegation token auth type
29 ResourcePrincipalDelegationToken AuthenticationType = "resource_principle_delegation_token"
30 // OAuth2DelegationToken is used for oauth delegation token auth type
31 OAuthDelegationToken AuthenticationType = "oauth_delegation_token"
32 // UnknownAuthenticationType is used for none meaningful auth type
33 UnknownAuthenticationType AuthenticationType = "unknown_auth_type"
34 )
35
36 // AuthConfig is used for getting auth related paras in config file
37 type AuthConfig struct {
38 AuthType AuthenticationType
39 // IsFromConfigFile is used to point out if the authConfig is from configuration file
40 IsFromConfigFile bool
41 OboToken *string
42 }
43
44 // ConfigurationProvider wraps information about the account owner
45 type ConfigurationProvider interface {
46 KeyProvider
47 TenancyOCID() (string, error)
48 UserOCID() (string, error)
49 KeyFingerprint() (string, error)
50 Region() (string, error)
51 // AuthType() is used for specify the needed auth type, like UserPrincipal, InstancePrincipal, etc.
52 AuthType() (AuthConfig, error)
53 }
54
55 var fileMutex = sync.Mutex{}
56 var fileCache = make(map[string][]byte)
57
58 // Reads the file contents from cache if present otherwise reads the file.
59 // If file to be read is frequently updated/refreshed, please use readFile(filename) as readFileFromCache(filename) might return the old contents from the cache.
60 func readFileFromCache(filename string) ([]byte, error) {
61 fileMutex.Lock()
62 defer fileMutex.Unlock()
63 val, ok := fileCache[filename]
64 if ok {
65 return val, nil
66 }
67 val, err := ioutil.ReadFile(filename)
68 if err == nil {
69 fileCache[filename] = val
70 }
71 return val, err
72 }
73
74 // Reads the file and returns the contents
75 func readFile(filename string) ([]byte, error) {
76 fileMutex.Lock()
77 defer fileMutex.Unlock()
78 val, err := os.ReadFile(filename)
79 return val, err
80 }
81
82 // IsConfigurationProviderValid Tests all parts of the configuration provider do not return an error, this method will
83 // not check AuthType(), since authType() is not required to be there.
84 func IsConfigurationProviderValid(conf ConfigurationProvider) (ok bool, err error) {
85 baseFn := []func() (string, error){conf.TenancyOCID, conf.UserOCID, conf.KeyFingerprint, conf.Region, conf.KeyID}
86 for _, fn := range baseFn {
87 _, err = fn()
88 ok = err == nil
89 if err != nil {
90 return
91 }
92 }
93
94 _, err = conf.PrivateRSAKey()
95 ok = err == nil
96 if err != nil {
97 return
98 }
99 return true, nil
100 }
101
102 // rawConfigurationProvider allows a user to simply construct a configuration provider from raw values.
103 type rawConfigurationProvider struct {
104 tenancy string
105 user string
106 region string
107 fingerprint string
108 privateKey string
109 privateKeyPassphrase *string
110 }
111
112 // NewRawConfigurationProvider will create a ConfigurationProvider with the arguments of the function
113 func NewRawConfigurationProvider(tenancy, user, region, fingerprint, privateKey string, privateKeyPassphrase *string) ConfigurationProvider {
114 return rawConfigurationProvider{tenancy, user, region, fingerprint, privateKey, privateKeyPassphrase}
115 }
116
117 func (p rawConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
118 return PrivateKeyFromBytes([]byte(p.privateKey), p.privateKeyPassphrase)
119 }
120
121 func (p rawConfigurationProvider) KeyID() (keyID string, err error) {
122 tenancy, err := p.TenancyOCID()
123 if err != nil {
124 return
125 }
126
127 user, err := p.UserOCID()
128 if err != nil {
129 return
130 }
131
132 fingerprint, err := p.KeyFingerprint()
133 if err != nil {
134 return
135 }
136
137 return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
138 }
139
140 func (p rawConfigurationProvider) TenancyOCID() (string, error) {
141 if p.tenancy == "" {
142 return "", fmt.Errorf("tenancy OCID can not be empty")
143 }
144 return p.tenancy, nil
145 }
146
147 func (p rawConfigurationProvider) UserOCID() (string, error) {
148 if p.user == "" {
149 return "", fmt.Errorf("user OCID can not be empty")
150 }
151 return p.user, nil
152 }
153
154 func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
155 if p.fingerprint == "" {
156 return "", fmt.Errorf("fingerprint can not be empty")
157 }
158 return p.fingerprint, nil
159 }
160
161 func (p rawConfigurationProvider) Region() (string, error) {
162 return canStringBeRegion(p.region)
163 }
164
165 func (p rawConfigurationProvider) AuthType() (AuthConfig, error) {
166 return AuthConfig{UnknownAuthenticationType, false, nil}, nil
167 }
168
169 // environmentConfigurationProvider reads configuration from environment variables
170 type environmentConfigurationProvider struct {
171 PrivateKeyPassword string
172 EnvironmentVariablePrefix string
173 }
174
175 // ConfigurationProviderEnvironmentVariables creates a ConfigurationProvider from a uniform set of environment variables starting with a prefix
176 // The env variables should look like: [prefix]_private_key_path, [prefix]_tenancy_ocid, [prefix]_user_ocid, [prefix]_fingerprint
177 // [prefix]_region
178 func ConfigurationProviderEnvironmentVariables(environmentVariablePrefix, privateKeyPassword string) ConfigurationProvider {
179 return environmentConfigurationProvider{EnvironmentVariablePrefix: environmentVariablePrefix,
180 PrivateKeyPassword: privateKeyPassword}
181 }
182
183 func (p environmentConfigurationProvider) String() string {
184 return fmt.Sprintf("Configuration provided by environment variables prefixed with: %s", p.EnvironmentVariablePrefix)
185 }
186
187 func (p environmentConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
188 environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "private_key_path")
189 var ok bool
190 var value string
191 if value, ok = os.LookupEnv(environmentVariable); !ok {
192 return nil, fmt.Errorf("can not read PrivateKey from env variable: %s", environmentVariable)
193 }
194
195 expandedPath := expandPath(value)
196 pemFileContent, err := readFileFromCache(expandedPath)
197 if err != nil {
198 Debugln("Can not read PrivateKey location from environment variable: " + environmentVariable)
199 return
200 }
201
202 key, err = PrivateKeyFromBytes(pemFileContent, &p.PrivateKeyPassword)
203 return
204 }
205
206 func (p environmentConfigurationProvider) KeyID() (keyID string, err error) {
207 ocid, err := p.TenancyOCID()
208 if err != nil {
209 return
210 }
211
212 userocid, err := p.UserOCID()
213 if err != nil {
214 return
215 }
216
217 fingerprint, err := p.KeyFingerprint()
218 if err != nil {
219 return
220 }
221
222 return fmt.Sprintf("%s/%s/%s", ocid, userocid, fingerprint), nil
223 }
224
225 func (p environmentConfigurationProvider) TenancyOCID() (value string, err error) {
226 environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "tenancy_ocid")
227 var ok bool
228 if value, ok = os.LookupEnv(environmentVariable); !ok {
229 err = fmt.Errorf("can not read Tenancy from environment variable %s", environmentVariable)
230 } else if value == "" {
231 err = fmt.Errorf("tenancy OCID can not be empty when reading from environmental variable")
232 }
233 return
234 }
235
236 func (p environmentConfigurationProvider) UserOCID() (value string, err error) {
237 environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "user_ocid")
238 var ok bool
239 if value, ok = os.LookupEnv(environmentVariable); !ok {
240 err = fmt.Errorf("can not read user id from environment variable %s", environmentVariable)
241 } else if value == "" {
242 err = fmt.Errorf("user OCID can not be empty when reading from environmental variable")
243 }
244 return
245 }
246
247 func (p environmentConfigurationProvider) KeyFingerprint() (value string, err error) {
248 environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "fingerprint")
249 var ok bool
250 if value, ok = os.LookupEnv(environmentVariable); !ok {
251 err = fmt.Errorf("can not read fingerprint from environment variable %s", environmentVariable)
252 } else if value == "" {
253 err = fmt.Errorf("fingerprint can not be empty when reading from environmental variable")
254 }
255 return
256 }
257
258 func (p environmentConfigurationProvider) Region() (value string, err error) {
259 environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "region")
260 var ok bool
261 if value, ok = os.LookupEnv(environmentVariable); !ok {
262 err = fmt.Errorf("can not read region from environment variable %s", environmentVariable)
263 return value, err
264 }
265
266 return canStringBeRegion(value)
267 }
268
269 func (p environmentConfigurationProvider) AuthType() (AuthConfig, error) {
270 return AuthConfig{UnknownAuthenticationType, false, nil},
271 fmt.Errorf("unsupported, keep the interface")
272 }
273
274 // fileConfigurationProvider. reads configuration information from a file
275 type fileConfigurationProvider struct {
276 //The path to the configuration file
277 ConfigPath string
278
279 //The password for the private key
280 PrivateKeyPassword string
281
282 //The profile for the configuration
283 Profile string
284
285 //ConfigFileInfo
286 FileInfo *configFileInfo
287
288 //Mutex to protect the config file
289 configMux sync.Mutex
290 }
291
292 type fileConfigurationProviderError struct {
293 err error
294 }
295
296 func (fpe fileConfigurationProviderError) Error() string {
297 return fmt.Sprintf("%s\nFor more info about config file and how to get required information, see https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm", fpe.err)
298 }
299
300 // ConfigurationProviderFromFile creates a configuration provider from a configuration file
301 // by reading the "DEFAULT" profile
302 func ConfigurationProviderFromFile(configFilePath, privateKeyPassword string) (ConfigurationProvider, error) {
303 if configFilePath == "" {
304 return nil, fmt.Errorf("config file path can not be empty")
305 }
306
307 return fileConfigurationProvider{
308 ConfigPath: configFilePath,
309 PrivateKeyPassword: privateKeyPassword,
310 Profile: "DEFAULT",
311 configMux: sync.Mutex{}}, nil
312 }
313
314 // ConfigurationProviderFromFileWithProfile creates a configuration provider from a configuration file
315 // and the given profile
316 func ConfigurationProviderFromFileWithProfile(configFilePath, profile, privateKeyPassword string) (ConfigurationProvider, error) {
317 if configFilePath == "" {
318 return nil, fileConfigurationProviderError{err: fmt.Errorf("config file path can not be empty")}
319 }
320
321 return fileConfigurationProvider{
322 ConfigPath: configFilePath,
323 PrivateKeyPassword: privateKeyPassword,
324 Profile: profile,
325 configMux: sync.Mutex{}}, nil
326 }
327
328 type configFileInfo struct {
329 UserOcid, Fingerprint, KeyFilePath, TenancyOcid, Region, Passphrase, SecurityTokenFilePath, DelegationTokenFilePath,
330 AuthenticationType string
331 PresentConfiguration rune
332 }
333
334 const (
335 hasTenancy = 1 << iota
336 hasUser
337 hasFingerprint
338 hasRegion
339 hasKeyFile
340 hasPassphrase
341 hasSecurityTokenFile
342 hasDelegationTokenFile
343 hasAuthenticationType
344 none
345 )
346
347 var profileRegex = regexp.MustCompile(`^\[(.*)\]`)
348
349 func parseConfigFile(data []byte, profile string) (info *configFileInfo, err error) {
350
351 if len(data) == 0 {
352 return nil, fileConfigurationProviderError{err: fmt.Errorf("configuration file content is empty")}
353 }
354
355 content := string(data)
356 splitContent := strings.Split(content, "\n")
357
358 //Look for profile
359 for i, line := range splitContent {
360 if match := profileRegex.FindStringSubmatch(line); len(match) > 1 && match[1] == profile {
361 start := i + 1
362 return parseConfigAtLine(start, splitContent)
363 }
364 }
365
366 return nil, fileConfigurationProviderError{err: fmt.Errorf("configuration file did not contain profile: %s", profile)}
367 }
368
369 func parseConfigAtLine(start int, content []string) (info *configFileInfo, err error) {
370 var configurationPresent rune
371 info = &configFileInfo{}
372 for i := start; i < len(content); i++ {
373 line := content[i]
374 if profileRegex.MatchString(line) {
375 break
376 }
377
378 if !strings.Contains(line, "=") {
379 continue
380 }
381
382 splits := strings.Split(line, "=")
383 switch key, value := strings.TrimSpace(splits[0]), strings.TrimSpace(splits[1]); strings.ToLower(key) {
384 case "passphrase", "pass_phrase":
385 configurationPresent = configurationPresent | hasPassphrase
386 info.Passphrase = value
387 case "user":
388 configurationPresent = configurationPresent | hasUser
389 info.UserOcid = value
390 case "fingerprint":
391 configurationPresent = configurationPresent | hasFingerprint
392 info.Fingerprint = value
393 case "key_file":
394 configurationPresent = configurationPresent | hasKeyFile
395 info.KeyFilePath = value
396 case "tenancy":
397 configurationPresent = configurationPresent | hasTenancy
398 info.TenancyOcid = value
399 case "region":
400 configurationPresent = configurationPresent | hasRegion
401 info.Region = value
402 case "security_token_file":
403 configurationPresent = configurationPresent | hasSecurityTokenFile
404 info.SecurityTokenFilePath = value
405 case "delegation_token_file":
406 configurationPresent = configurationPresent | hasDelegationTokenFile
407 info.DelegationTokenFilePath = value
408 case "authentication_type":
409 configurationPresent = configurationPresent | hasAuthenticationType
410 info.AuthenticationType = value
411 }
412 }
413 info.PresentConfiguration = configurationPresent
414 return
415
416 }
417
418 // cleans and expands the path if it contains a tilde , returns the expanded path or the input path as is if not expansion
419 // was performed
420 func expandPath(filename string) (expandedPath string) {
421 cleanedPath := filepath.Clean(filename)
422 expandedPath = cleanedPath
423 if strings.HasPrefix(cleanedPath, "~") {
424 rest := cleanedPath[2:]
425 expandedPath = filepath.Join(getHomeFolder(), rest)
426 }
427 return
428 }
429
430 func openConfigFile(configFilePath string) (data []byte, err error) {
431 expandedPath := expandPath(configFilePath)
432 data, err = readFileFromCache(expandedPath)
433 if err != nil {
434 err = fmt.Errorf("can not read config file: %s due to: %s", configFilePath, err.Error())
435 }
436
437 return
438 }
439
440 func (p fileConfigurationProvider) String() string {
441 return fmt.Sprintf("Configuration provided by file: %s", p.ConfigPath)
442 }
443
444 func (p fileConfigurationProvider) readAndParseConfigFile() (info *configFileInfo, err error) {
445 p.configMux.Lock()
446 defer p.configMux.Unlock()
447 if p.FileInfo != nil {
448 return p.FileInfo, nil
449 }
450
451 if p.ConfigPath == "" {
452 return nil, fileConfigurationProviderError{err: fmt.Errorf("configuration path can not be empty")}
453 }
454
455 data, err := openConfigFile(p.ConfigPath)
456 if err != nil {
457 err = fileConfigurationProviderError{err: fmt.Errorf("error while parsing config file: %s. Due to: %s", p.ConfigPath, err.Error())}
458 return
459 }
460
461 p.FileInfo, err = parseConfigFile(data, p.Profile)
462 return p.FileInfo, err
463 }
464
465 func presentOrError(value string, expectedConf, presentConf rune, confMissing string) (string, error) {
466 if presentConf&expectedConf == expectedConf {
467 return value, nil
468 }
469 return "", fileConfigurationProviderError{err: errors.New(confMissing + " configuration is missing from file")}
470 }
471
472 func (p fileConfigurationProvider) TenancyOCID() (value string, err error) {
473 info, err := p.readAndParseConfigFile()
474 if err != nil {
475 err = fileConfigurationProviderError{err: fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())}
476 return
477 }
478
479 value, err = presentOrError(info.TenancyOcid, hasTenancy, info.PresentConfiguration, "tenancy")
480 if err == nil && value == "" {
481 err = fileConfigurationProviderError{err: fmt.Errorf("tenancy OCID can not be empty when reading from config file")}
482 }
483 return
484 }
485
486 func (p fileConfigurationProvider) UserOCID() (value string, err error) {
487 info, err := p.readAndParseConfigFile()
488 if err != nil {
489 err = fileConfigurationProviderError{err: fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())}
490 return
491 }
492
493 if value, err = presentOrError(info.UserOcid, hasUser, info.PresentConfiguration, "user"); err != nil {
494 // need to check if securityTokenPath is provided, if security token is provided, userOCID can be "".
495 if _, stErr := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration,
496 "securityTokenPath"); stErr == nil {
497 err = nil
498 }
499 }
500 return
501 }
502
503 func (p fileConfigurationProvider) KeyFingerprint() (value string, err error) {
504 info, err := p.readAndParseConfigFile()
505 if err != nil {
506 err = fileConfigurationProviderError{err: fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())}
507 return
508 }
509 value, err = presentOrError(info.Fingerprint, hasFingerprint, info.PresentConfiguration, "fingerprint")
510 if err == nil && value == "" {
511 return "", fmt.Errorf("fingerprint can not be empty when reading from config file")
512 }
513 return
514 }
515
516 func (p fileConfigurationProvider) KeyID() (keyID string, err error) {
517 tenancy, err := p.TenancyOCID()
518 if err != nil {
519 return
520 }
521
522 fingerprint, err := p.KeyFingerprint()
523 if err != nil {
524 return
525 }
526
527 info, err := p.readAndParseConfigFile()
528 if err != nil {
529 err = fileConfigurationProviderError{err: fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())}
530 return
531 }
532 if info.PresentConfiguration&hasUser == hasUser {
533 if info.UserOcid == "" {
534 err = fileConfigurationProviderError{err: fmt.Errorf("user cannot be empty in the config file")}
535 return
536 }
537 return fmt.Sprintf("%s/%s/%s", tenancy, info.UserOcid, fingerprint), nil
538 }
539 filePath, pathErr := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration, "securityTokenFilePath")
540 if pathErr == nil {
541 rawString, err := getTokenContent(filePath)
542 if err != nil {
543 return "", fileConfigurationProviderError{err: err}
544 }
545 return "ST$" + rawString, nil
546 }
547 err = fileConfigurationProviderError{err: fmt.Errorf("can not read SecurityTokenFilePath from configuration file due to: %s", pathErr.Error())}
548 return
549 }
550
551 func (p fileConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
552 info, err := p.readAndParseConfigFile()
553 if err != nil {
554 err = fileConfigurationProviderError{err: fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())}
555 return
556 }
557
558 filePath, err := presentOrError(info.KeyFilePath, hasKeyFile, info.PresentConfiguration, "key file path")
559 if err != nil {
560 return
561 }
562
563 expandedPath := expandPath(filePath)
564 pemFileContent, err := readFileFromCache(expandedPath)
565 if err != nil {
566 err = fileConfigurationProviderError{err: fmt.Errorf("can not read PrivateKey from configuration file due to: %s", err.Error())}
567 return
568 }
569
570 password := p.PrivateKeyPassword
571
572 if password == "" && ((info.PresentConfiguration & hasPassphrase) == hasPassphrase) {
573 password = info.Passphrase
574 }
575
576 key, err = PrivateKeyFromBytes(pemFileContent, &password)
577 return
578 }
579
580 func (p fileConfigurationProvider) Region() (value string, err error) {
581 info, err := p.readAndParseConfigFile()
582 if err != nil {
583 err = fileConfigurationProviderError{err: fmt.Errorf("can not read region configuration due to: %s", err.Error())}
584 return
585 }
586
587 value, err = presentOrError(info.Region, hasRegion, info.PresentConfiguration, "region")
588 if err != nil {
589 val, error := getRegionFromEnvVar()
590 if error != nil {
591 err = fileConfigurationProviderError{err: fmt.Errorf("region configuration is missing from file, nor for OCI_REGION env var")}
592 return
593 }
594 value = val
595 }
596
597 return canStringBeRegion(value)
598 }
599
600 func (p fileConfigurationProvider) AuthType() (AuthConfig, error) {
601 info, err := p.readAndParseConfigFile()
602 if err != nil {
603 err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
604 return AuthConfig{UnknownAuthenticationType, true, nil}, err
605 }
606 val, _ := presentOrError(info.AuthenticationType, hasAuthenticationType, info.PresentConfiguration, "authentication_type")
607
608 if val == "instance_principal" {
609 if filePath, err := presentOrError(info.DelegationTokenFilePath, hasDelegationTokenFile, info.PresentConfiguration, "delegationTokenFilePath"); err == nil {
610 if delegationToken, err := getTokenContent(filePath); err == nil && delegationToken != "" {
611 Debugf("delegation token content is %s, and error is %s ", delegationToken, err)
612 return AuthConfig{InstancePrincipalDelegationToken, true, &delegationToken}, nil
613 }
614 return AuthConfig{UnknownAuthenticationType, true, nil}, err
615
616 }
617 // normal instance principle
618 return AuthConfig{InstancePrincipal, true, nil}, nil
619 }
620
621 // by default, if no "authentication_type" is provided, just treated as user principle type, and will not return error
622 return AuthConfig{UserPrincipal, true, nil}, nil
623 }
624
625 func getTokenContent(filePath string) (string, error) {
626 expandedPath := expandPath(filePath)
627 tokenFileContent, err := readFile(expandedPath)
628 if err != nil {
629 err = fileConfigurationProviderError{err: fmt.Errorf("can not read token content from configuration file due to: %s", err.Error())}
630 return "", err
631 }
632 return string(tokenFileContent), nil
633 }
634
635 // A configuration provider that look for information in multiple configuration providers
636 type composingConfigurationProvider struct {
637 Providers []ConfigurationProvider
638 }
639
640 // ComposingConfigurationProvider creates a composing configuration provider with the given slice of configuration providers
641 // A composing provider will return the configuration of the first provider that has the required property
642 // if no provider has the property it will return an error.
643 func ComposingConfigurationProvider(providers []ConfigurationProvider) (ConfigurationProvider, error) {
644 if len(providers) == 0 {
645 return nil, fmt.Errorf("providers can not be an empty slice")
646 }
647
648 for i, p := range providers {
649 if p == nil {
650 return nil, fmt.Errorf("provider in position: %d is nil. ComposingConfiurationProvider does not support nil values", i)
651 }
652 }
653 return composingConfigurationProvider{Providers: providers}, nil
654 }
655
656 func (c composingConfigurationProvider) TenancyOCID() (string, error) {
657 for _, p := range c.Providers {
658 val, err := p.TenancyOCID()
659 if err == nil {
660 return val, nil
661 }
662 Debugf("did not find a proper configuration for tenancy, err: %v", err)
663 }
664 return "", fmt.Errorf("did not find a proper configuration for tenancy")
665 }
666
667 func (c composingConfigurationProvider) UserOCID() (string, error) {
668 for _, p := range c.Providers {
669 val, err := p.UserOCID()
670 if err == nil {
671 return val, nil
672 }
673 Debugf("did not find a proper configuration for keyFingerprint, err: %v", err)
674 }
675 return "", fmt.Errorf("did not find a proper configuration for user")
676 }
677
678 func (c composingConfigurationProvider) KeyFingerprint() (string, error) {
679 for _, p := range c.Providers {
680 val, err := p.KeyFingerprint()
681 if err == nil {
682 return val, nil
683 }
684 }
685 return "", fmt.Errorf("did not find a proper configuration for keyFingerprint")
686 }
687 func (c composingConfigurationProvider) Region() (string, error) {
688 for _, p := range c.Providers {
689 val, err := p.Region()
690 if err == nil {
691 return val, nil
692 }
693 }
694 if val, err := getRegionFromEnvVar(); err == nil {
695 return val, nil
696 }
697 return "", fmt.Errorf("did not find a proper configuration for region, nor for OCI_REGION env var")
698 }
699
700 func (c composingConfigurationProvider) KeyID() (string, error) {
701 for _, p := range c.Providers {
702 val, err := p.KeyID()
703 if err == nil {
704 return val, nil
705 }
706 }
707 return "", fmt.Errorf("did not find a proper configuration for key id")
708 }
709
710 func (c composingConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
711 for _, p := range c.Providers {
712 val, err := p.PrivateRSAKey()
713 if err == nil {
714 return val, nil
715 }
716 }
717 return nil, fmt.Errorf("did not find a proper configuration for private key")
718 }
719
720 func (c composingConfigurationProvider) AuthType() (AuthConfig, error) {
721 // only check the first default fileConfigProvider
722 authConfig, err := c.Providers[0].AuthType()
723 if err == nil && authConfig.AuthType != UnknownAuthenticationType {
724 return authConfig, nil
725 }
726 return AuthConfig{UnknownAuthenticationType, false, nil}, fmt.Errorf("did not find a proper configuration for auth type")
727 }
728
729 func getRegionFromEnvVar() (string, error) {
730 regionEnvVar := "OCI_REGION"
731 if region, existed := os.LookupEnv(regionEnvVar); existed {
732 return region, nil
733 }
734 return "", fmt.Errorf("did not find OCI_REGION env var")
735 }
736
737 type sessionTokenConfigurationProvider struct {
738 fileConfigurationProvider
739 }
740
741 func (p sessionTokenConfigurationProvider) UserOCID() (value string, err error) {
742 info, err := p.readAndParseConfigFile()
743 if err != nil {
744 err = fileConfigurationProviderError{err: fmt.Errorf("can not read the configuration due to: %s", err.Error())}
745 return
746 }
747 // In case of session token-based authentication, userOCID will not be present
748 // need to check if session token path is provided in the configuration
749 if _, stErr := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration,
750 "securityTokenPath"); stErr == nil {
751 err = nil
752 }
753 return
754 }
755
756 func (p sessionTokenConfigurationProvider) KeyID() (keyID string, err error) {
757 _, err = p.TenancyOCID()
758 if err != nil {
759 return
760 }
761
762 _, err = p.KeyFingerprint()
763 if err != nil {
764 return
765 }
766
767 info, err := p.readAndParseConfigFile()
768 if err != nil {
769 err = fileConfigurationProviderError{err: fmt.Errorf("can not read SessionTokenFilePath configuration due to: %s", err.Error())}
770 return
771 }
772
773 filePath, pathErr := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration, "securityTokenFilePath")
774 if pathErr == nil {
775 rawString, err := getTokenContent(filePath)
776 if err != nil {
777 return "", fileConfigurationProviderError{err: err}
778 }
779 return "ST$" + rawString, nil
780 }
781 err = fileConfigurationProviderError{err: fmt.Errorf("can not read SessionTokenFilePath from configuration file due to: %s", pathErr.Error())}
782 return
783 }
784
785 // ConfigurationProviderForSessionToken creates a session token configuration provider from a configuration file
786 // by reading the "DEFAULT" profile
787 func ConfigurationProviderForSessionToken(configFilePath, privateKeyPassword string) (ConfigurationProvider, error) {
788 if configFilePath == "" {
789 return nil, fileConfigurationProviderError{err: fmt.Errorf("config file path can not be empty")}
790 }
791
792 return sessionTokenConfigurationProvider{
793 fileConfigurationProvider{
794 ConfigPath: configFilePath,
795 PrivateKeyPassword: privateKeyPassword,
796 Profile: "DEFAULT",
797 configMux: sync.Mutex{}}}, nil
798 }
799
800 // ConfigurationProviderForSessionTokenWithProfile creates a session token configuration provider from a configuration file
801 // by reading the given profile
802 func ConfigurationProviderForSessionTokenWithProfile(configFilePath, profile, privateKeyPassword string) (ConfigurationProvider, error) {
803 if configFilePath == "" {
804 return nil, fileConfigurationProviderError{err: fmt.Errorf("config file path can not be empty")}
805 }
806
807 return sessionTokenConfigurationProvider{
808 fileConfigurationProvider{
809 ConfigPath: configFilePath,
810 PrivateKeyPassword: privateKeyPassword,
811 Profile: profile,
812 configMux: sync.Mutex{}}}, nil
813 }
814
815 func (p sessionTokenConfigurationProvider) Refreshable() bool {
816 return true
817 }
818
819 // RefreshableConfigurationProvider the interface to identity if the config provider is refreshable
820 type RefreshableConfigurationProvider interface {
821 Refreshable() bool
822 }
823