1package credentials
2
3import (
4	"github.com/aws/aws-sdk-go/aws/awserr"
5)
6
7var (
8	// ErrNoValidProvidersFoundInChain Is returned when there are no valid
9	// providers in the ChainProvider.
10	//
11	// This has been deprecated. For verbose error messaging set
12	// aws.Config.CredentialsChainVerboseErrors to true.
13	ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders",
14		`no valid providers in chain. Deprecated.
15	For verbose messaging see aws.Config.CredentialsChainVerboseErrors`,
16		nil)
17)
18
19// A ChainProvider will search for a provider which returns credentials
20// and cache that provider until Retrieve is called again.
21//
22// The ChainProvider provides a way of chaining multiple providers together
23// which will pick the first available using priority order of the Providers
24// in the list.
25//
26// If none of the Providers retrieve valid credentials Value, ChainProvider's
27// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
28//
29// If a Provider is found which returns valid credentials Value ChainProvider
30// will cache that Provider for all calls to IsExpired(), until Retrieve is
31// called again.
32//
33// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
34// In this example EnvProvider will first check if any credentials are available
35// via the environment variables. If there are none ChainProvider will check
36// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
37// does not return any credentials ChainProvider will return the error
38// ErrNoValidProvidersFoundInChain
39//
40//     creds := credentials.NewChainCredentials(
41//         []credentials.Provider{
42//             &credentials.EnvProvider{},
43//             &ec2rolecreds.EC2RoleProvider{
44//                 Client: ec2metadata.New(sess),
45//             },
46//         })
47//
48//     // Usage of ChainCredentials with aws.Config
49//     svc := ec2.New(session.Must(session.NewSession(&aws.Config{
50//       Credentials: creds,
51//     })))
52//
53type ChainProvider struct {
54	Providers     []Provider
55	curr          Provider
56	VerboseErrors bool
57}
58
59// NewChainCredentials returns a pointer to a new Credentials object
60// wrapping a chain of providers.
61func NewChainCredentials(providers []Provider) *Credentials {
62	return NewCredentials(&ChainProvider{
63		Providers: append([]Provider{}, providers...),
64	})
65}
66
67// Retrieve returns the credentials value or error if no provider returned
68// without error.
69//
70// If a provider is found it will be cached and any calls to IsExpired()
71// will return the expired state of the cached provider.
72func (c *ChainProvider) Retrieve() (Value, error) {
73	var errs []error
74	for _, p := range c.Providers {
75		creds, err := p.Retrieve()
76		if err == nil {
77			c.curr = p
78			return creds, nil
79		}
80		errs = append(errs, err)
81	}
82	c.curr = nil
83
84	var err error
85	err = ErrNoValidProvidersFoundInChain
86	if c.VerboseErrors {
87		err = awserr.NewBatchError("NoCredentialProviders", "no valid providers in chain", errs)
88	}
89	return Value{}, err
90}
91
92// IsExpired will returned the expired state of the currently cached provider
93// if there is one.  If there is no current provider, true will be returned.
94func (c *ChainProvider) IsExpired() bool {
95	if c.curr != nil {
96		return c.curr.IsExpired()
97	}
98
99	return true
100}
101