| // Package credentials provides credential retrieval and management |
| // |
| // The Credentials is the primary method of getting access to and managing |
| // credentials Values. Using dependency injection retrieval of the credential |
| // values is handled by a object which satisfies the Provider interface. |
| // |
| // By default the Credentials.Get() will cache the successful result of a |
| // Provider's Retrieve() until Provider.IsExpired() returns true. At which |
| // point Credentials will call Provider's Retrieve() to get new credential Value. |
| // |
| // The Provider is responsible for determining when credentials Value have expired. |
| // It is also important to note that Credentials will always call Retrieve the |
| // first time Credentials.Get() is called. |
| // |
| // Example of using the environment variable credentials. |
| // |
| // creds := credentials.NewEnvCredentials() |
| // |
| // // Retrieve the credentials value |
| // credValue, err := creds.Get() |
| // if err != nil { |
| // // handle error |
| // } |
| // |
| // Example of forcing credentials to expire and be refreshed on the next Get(). |
| // This may be helpful to proactively expire credentials and refresh them sooner |
| // than they would naturally expire on their own. |
| // |
| // creds := credentials.NewCredentials(&ec2rolecreds.EC2RoleProvider{}) |
| // creds.Expire() |
| // credsValue, err := creds.Get() |
| // // New credentials will be retrieved instead of from cache. |
| // |
| // |
| // Custom Provider |
| // |
| // Each Provider built into this package also provides a helper method to generate |
| // a Credentials pointer setup with the provider. To use a custom Provider just |
| // create a type which satisfies the Provider interface and pass it to the |
| // NewCredentials method. |
| // |
| // type MyProvider struct{} |
| // func (m *MyProvider) Retrieve() (Value, error) {...} |
| // func (m *MyProvider) IsExpired() bool {...} |
| // |
| // creds := credentials.NewCredentials(&MyProvider{}) |
| // credValue, err := creds.Get() |
| // |
| package credentials |
| |
| import ( |
| "fmt" |
| "sync" |
| "time" |
| |
| "github.com/aws/aws-sdk-go/aws/awserr" |
| ) |
| |
| // AnonymousCredentials is an empty Credential object that can be used as |
| // dummy placeholder credentials for requests that do not need signed. |
| // |
| // This Credentials can be used to configure a service to not sign requests |
| // when making service API calls. For example, when accessing public |
| // s3 buckets. |
| // |
| // svc := s3.New(session.Must(session.NewSession(&aws.Config{ |
| // Credentials: credentials.AnonymousCredentials, |
| // }))) |
| // // Access public S3 buckets. |
| var AnonymousCredentials = NewStaticCredentials("", "", "") |
| |
| // A Value is the AWS credentials value for individual credential fields. |
| type Value struct { |
| // AWS Access key ID |
| AccessKeyID string |
| |
| // AWS Secret Access Key |
| SecretAccessKey string |
| |
| // AWS Session Token |
| SessionToken string |
| |
| // Provider used to get credentials |
| ProviderName string |
| } |
| |
| // HasKeys returns if the credentials Value has both AccessKeyID and |
| // SecretAccessKey value set. |
| func (v Value) HasKeys() bool { |
| return len(v.AccessKeyID) != 0 && len(v.SecretAccessKey) != 0 |
| } |
| |
| // A Provider is the interface for any component which will provide credentials |
| // Value. A provider is required to manage its own Expired state, and what to |
| // be expired means. |
| // |
| // The Provider should not need to implement its own mutexes, because |
| // that will be managed by Credentials. |
| type Provider interface { |
| // Retrieve returns nil if it successfully retrieved the value. |
| // Error is returned if the value were not obtainable, or empty. |
| Retrieve() (Value, error) |
| |
| // IsExpired returns if the credentials are no longer valid, and need |
| // to be retrieved. |
| IsExpired() bool |
| } |
| |
| // An Expirer is an interface that Providers can implement to expose the expiration |
| // time, if known. If the Provider cannot accurately provide this info, |
| // it should not implement this interface. |
| type Expirer interface { |
| // The time at which the credentials are no longer valid |
| ExpiresAt() time.Time |
| } |
| |
| // An ErrorProvider is a stub credentials provider that always returns an error |
| // this is used by the SDK when construction a known provider is not possible |
| // due to an error. |
| type ErrorProvider struct { |
| // The error to be returned from Retrieve |
| Err error |
| |
| // The provider name to set on the Retrieved returned Value |
| ProviderName string |
| } |
| |
| // Retrieve will always return the error that the ErrorProvider was created with. |
| func (p ErrorProvider) Retrieve() (Value, error) { |
| return Value{ProviderName: p.ProviderName}, p.Err |
| } |
| |
| // IsExpired will always return not expired. |
| func (p ErrorProvider) IsExpired() bool { |
| return false |
| } |
| |
| // A Expiry provides shared expiration logic to be used by credentials |
| // providers to implement expiry functionality. |
| // |
| // The best method to use this struct is as an anonymous field within the |
| // provider's struct. |
| // |
| // Example: |
| // type EC2RoleProvider struct { |
| // Expiry |
| // ... |
| // } |
| type Expiry struct { |
| // The date/time when to expire on |
| expiration time.Time |
| |
| // If set will be used by IsExpired to determine the current time. |
| // Defaults to time.Now if CurrentTime is not set. Available for testing |
| // to be able to mock out the current time. |
| CurrentTime func() time.Time |
| } |
| |
| // SetExpiration sets the expiration IsExpired will check when called. |
| // |
| // If window is greater than 0 the expiration time will be reduced by the |
| // window value. |
| // |
| // Using a window is helpful to trigger credentials to expire sooner than |
| // the expiration time given to ensure no requests are made with expired |
| // tokens. |
| func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) { |
| e.expiration = expiration |
| if window > 0 { |
| e.expiration = e.expiration.Add(-window) |
| } |
| } |
| |
| // IsExpired returns if the credentials are expired. |
| func (e *Expiry) IsExpired() bool { |
| curTime := e.CurrentTime |
| if curTime == nil { |
| curTime = time.Now |
| } |
| return e.expiration.Before(curTime()) |
| } |
| |
| // ExpiresAt returns the expiration time of the credential |
| func (e *Expiry) ExpiresAt() time.Time { |
| return e.expiration |
| } |
| |
| // A Credentials provides concurrency safe retrieval of AWS credentials Value. |
| // Credentials will cache the credentials value until they expire. Once the value |
| // expires the next Get will attempt to retrieve valid credentials. |
| // |
| // Credentials is safe to use across multiple goroutines and will manage the |
| // synchronous state so the Providers do not need to implement their own |
| // synchronization. |
| // |
| // The first Credentials.Get() will always call Provider.Retrieve() to get the |
| // first instance of the credentials Value. All calls to Get() after that |
| // will return the cached credentials Value until IsExpired() returns true. |
| type Credentials struct { |
| creds Value |
| forceRefresh bool |
| |
| m sync.RWMutex |
| |
| provider Provider |
| } |
| |
| // NewCredentials returns a pointer to a new Credentials with the provider set. |
| func NewCredentials(provider Provider) *Credentials { |
| return &Credentials{ |
| provider: provider, |
| forceRefresh: true, |
| } |
| } |
| |
| // Get returns the credentials value, or error if the credentials Value failed |
| // to be retrieved. |
| // |
| // Will return the cached credentials Value if it has not expired. If the |
| // credentials Value has expired the Provider's Retrieve() will be called |
| // to refresh the credentials. |
| // |
| // If Credentials.Expire() was called the credentials Value will be force |
| // expired, and the next call to Get() will cause them to be refreshed. |
| func (c *Credentials) Get() (Value, error) { |
| // Check the cached credentials first with just the read lock. |
| c.m.RLock() |
| if !c.isExpired() { |
| creds := c.creds |
| c.m.RUnlock() |
| return creds, nil |
| } |
| c.m.RUnlock() |
| |
| // Credentials are expired need to retrieve the credentials taking the full |
| // lock. |
| c.m.Lock() |
| defer c.m.Unlock() |
| |
| if c.isExpired() { |
| creds, err := c.provider.Retrieve() |
| if err != nil { |
| return Value{}, err |
| } |
| c.creds = creds |
| c.forceRefresh = false |
| } |
| |
| return c.creds, nil |
| } |
| |
| // Expire expires the credentials and forces them to be retrieved on the |
| // next call to Get(). |
| // |
| // This will override the Provider's expired state, and force Credentials |
| // to call the Provider's Retrieve(). |
| func (c *Credentials) Expire() { |
| c.m.Lock() |
| defer c.m.Unlock() |
| |
| c.forceRefresh = true |
| } |
| |
| // IsExpired returns if the credentials are no longer valid, and need |
| // to be retrieved. |
| // |
| // If the Credentials were forced to be expired with Expire() this will |
| // reflect that override. |
| func (c *Credentials) IsExpired() bool { |
| c.m.RLock() |
| defer c.m.RUnlock() |
| |
| return c.isExpired() |
| } |
| |
| // isExpired helper method wrapping the definition of expired credentials. |
| func (c *Credentials) isExpired() bool { |
| return c.forceRefresh || c.provider.IsExpired() |
| } |
| |
| // ExpiresAt provides access to the functionality of the Expirer interface of |
| // the underlying Provider, if it supports that interface. Otherwise, it returns |
| // an error. |
| func (c *Credentials) ExpiresAt() (time.Time, error) { |
| c.m.RLock() |
| defer c.m.RUnlock() |
| |
| expirer, ok := c.provider.(Expirer) |
| if !ok { |
| return time.Time{}, awserr.New("ProviderNotExpirer", |
| fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName), |
| nil) |
| } |
| if c.forceRefresh { |
| // set expiration time to the distant past |
| return time.Time{}, nil |
| } |
| return expirer.ExpiresAt(), nil |
| } |