1// Package credentials provides credential retrieval and management 2// 3// The Credentials is the primary method of getting access to and managing 4// credentials Values. Using dependency injection retrieval of the credential 5// values is handled by a object which satisfies the Provider interface. 6// 7// By default the Credentials.Get() will cache the successful result of a 8// Provider's Retrieve() until Provider.IsExpired() returns true. At which 9// point Credentials will call Provider's Retrieve() to get new credential Value. 10// 11// The Provider is responsible for determining when credentials Value have expired. 12// It is also important to note that Credentials will always call Retrieve the 13// first time Credentials.Get() is called. 14// 15// Example of using the environment variable credentials. 16// 17// creds := credentials.NewEnvCredentials() 18// 19// // Retrieve the credentials value 20// credValue, err := creds.Get() 21// if err != nil { 22// // handle error 23// } 24// 25// Example of forcing credentials to expire and be refreshed on the next Get(). 26// This may be helpful to proactively expire credentials and refresh them sooner 27// than they would naturally expire on their own. 28// 29// creds := credentials.NewCredentials(&ec2rolecreds.EC2RoleProvider{}) 30// creds.Expire() 31// credsValue, err := creds.Get() 32// // New credentials will be retrieved instead of from cache. 33// 34// 35// Custom Provider 36// 37// Each Provider built into this package also provides a helper method to generate 38// a Credentials pointer setup with the provider. To use a custom Provider just 39// create a type which satisfies the Provider interface and pass it to the 40// NewCredentials method. 41// 42// type MyProvider struct{} 43// func (m *MyProvider) Retrieve() (Value, error) {...} 44// func (m *MyProvider) IsExpired() bool {...} 45// 46// creds := credentials.NewCredentials(&MyProvider{}) 47// credValue, err := creds.Get() 48// 49package credentials 50 51import ( 52 "fmt" 53 "sync" 54 "time" 55 56 "github.com/aws/aws-sdk-go/aws/awserr" 57) 58 59// AnonymousCredentials is an empty Credential object that can be used as 60// dummy placeholder credentials for requests that do not need signed. 61// 62// This Credentials can be used to configure a service to not sign requests 63// when making service API calls. For example, when accessing public 64// s3 buckets. 65// 66// svc := s3.New(session.Must(session.NewSession(&aws.Config{ 67// Credentials: credentials.AnonymousCredentials, 68// }))) 69// // Access public S3 buckets. 70var AnonymousCredentials = NewStaticCredentials("", "", "") 71 72// A Value is the AWS credentials value for individual credential fields. 73type Value struct { 74 // AWS Access key ID 75 AccessKeyID string 76 77 // AWS Secret Access Key 78 SecretAccessKey string 79 80 // AWS Session Token 81 SessionToken string 82 83 // Provider used to get credentials 84 ProviderName string 85} 86 87// HasKeys returns if the credentials Value has both AccessKeyID and 88// SecretAccessKey value set. 89func (v Value) HasKeys() bool { 90 return len(v.AccessKeyID) != 0 && len(v.SecretAccessKey) != 0 91} 92 93// A Provider is the interface for any component which will provide credentials 94// Value. A provider is required to manage its own Expired state, and what to 95// be expired means. 96// 97// The Provider should not need to implement its own mutexes, because 98// that will be managed by Credentials. 99type Provider interface { 100 // Retrieve returns nil if it successfully retrieved the value. 101 // Error is returned if the value were not obtainable, or empty. 102 Retrieve() (Value, error) 103 104 // IsExpired returns if the credentials are no longer valid, and need 105 // to be retrieved. 106 IsExpired() bool 107} 108 109// An Expirer is an interface that Providers can implement to expose the expiration 110// time, if known. If the Provider cannot accurately provide this info, 111// it should not implement this interface. 112type Expirer interface { 113 // The time at which the credentials are no longer valid 114 ExpiresAt() time.Time 115} 116 117// An ErrorProvider is a stub credentials provider that always returns an error 118// this is used by the SDK when construction a known provider is not possible 119// due to an error. 120type ErrorProvider struct { 121 // The error to be returned from Retrieve 122 Err error 123 124 // The provider name to set on the Retrieved returned Value 125 ProviderName string 126} 127 128// Retrieve will always return the error that the ErrorProvider was created with. 129func (p ErrorProvider) Retrieve() (Value, error) { 130 return Value{ProviderName: p.ProviderName}, p.Err 131} 132 133// IsExpired will always return not expired. 134func (p ErrorProvider) IsExpired() bool { 135 return false 136} 137 138// A Expiry provides shared expiration logic to be used by credentials 139// providers to implement expiry functionality. 140// 141// The best method to use this struct is as an anonymous field within the 142// provider's struct. 143// 144// Example: 145// type EC2RoleProvider struct { 146// Expiry 147// ... 148// } 149type Expiry struct { 150 // The date/time when to expire on 151 expiration time.Time 152 153 // If set will be used by IsExpired to determine the current time. 154 // Defaults to time.Now if CurrentTime is not set. Available for testing 155 // to be able to mock out the current time. 156 CurrentTime func() time.Time 157} 158 159// SetExpiration sets the expiration IsExpired will check when called. 160// 161// If window is greater than 0 the expiration time will be reduced by the 162// window value. 163// 164// Using a window is helpful to trigger credentials to expire sooner than 165// the expiration time given to ensure no requests are made with expired 166// tokens. 167func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) { 168 e.expiration = expiration 169 if window > 0 { 170 e.expiration = e.expiration.Add(-window) 171 } 172} 173 174// IsExpired returns if the credentials are expired. 175func (e *Expiry) IsExpired() bool { 176 curTime := e.CurrentTime 177 if curTime == nil { 178 curTime = time.Now 179 } 180 return e.expiration.Before(curTime()) 181} 182 183// ExpiresAt returns the expiration time of the credential 184func (e *Expiry) ExpiresAt() time.Time { 185 return e.expiration 186} 187 188// A Credentials provides concurrency safe retrieval of AWS credentials Value. 189// Credentials will cache the credentials value until they expire. Once the value 190// expires the next Get will attempt to retrieve valid credentials. 191// 192// Credentials is safe to use across multiple goroutines and will manage the 193// synchronous state so the Providers do not need to implement their own 194// synchronization. 195// 196// The first Credentials.Get() will always call Provider.Retrieve() to get the 197// first instance of the credentials Value. All calls to Get() after that 198// will return the cached credentials Value until IsExpired() returns true. 199type Credentials struct { 200 creds Value 201 forceRefresh bool 202 203 m sync.RWMutex 204 205 provider Provider 206} 207 208// NewCredentials returns a pointer to a new Credentials with the provider set. 209func NewCredentials(provider Provider) *Credentials { 210 return &Credentials{ 211 provider: provider, 212 forceRefresh: true, 213 } 214} 215 216// Get returns the credentials value, or error if the credentials Value failed 217// to be retrieved. 218// 219// Will return the cached credentials Value if it has not expired. If the 220// credentials Value has expired the Provider's Retrieve() will be called 221// to refresh the credentials. 222// 223// If Credentials.Expire() was called the credentials Value will be force 224// expired, and the next call to Get() will cause them to be refreshed. 225func (c *Credentials) Get() (Value, error) { 226 // Check the cached credentials first with just the read lock. 227 c.m.RLock() 228 if !c.isExpired() { 229 creds := c.creds 230 c.m.RUnlock() 231 return creds, nil 232 } 233 c.m.RUnlock() 234 235 // Credentials are expired need to retrieve the credentials taking the full 236 // lock. 237 c.m.Lock() 238 defer c.m.Unlock() 239 240 if c.isExpired() { 241 creds, err := c.provider.Retrieve() 242 if err != nil { 243 return Value{}, err 244 } 245 c.creds = creds 246 c.forceRefresh = false 247 } 248 249 return c.creds, nil 250} 251 252// Expire expires the credentials and forces them to be retrieved on the 253// next call to Get(). 254// 255// This will override the Provider's expired state, and force Credentials 256// to call the Provider's Retrieve(). 257func (c *Credentials) Expire() { 258 c.m.Lock() 259 defer c.m.Unlock() 260 261 c.forceRefresh = true 262} 263 264// IsExpired returns if the credentials are no longer valid, and need 265// to be retrieved. 266// 267// If the Credentials were forced to be expired with Expire() this will 268// reflect that override. 269func (c *Credentials) IsExpired() bool { 270 c.m.RLock() 271 defer c.m.RUnlock() 272 273 return c.isExpired() 274} 275 276// isExpired helper method wrapping the definition of expired credentials. 277func (c *Credentials) isExpired() bool { 278 return c.forceRefresh || c.provider.IsExpired() 279} 280 281// ExpiresAt provides access to the functionality of the Expirer interface of 282// the underlying Provider, if it supports that interface. Otherwise, it returns 283// an error. 284func (c *Credentials) ExpiresAt() (time.Time, error) { 285 c.m.RLock() 286 defer c.m.RUnlock() 287 288 expirer, ok := c.provider.(Expirer) 289 if !ok { 290 return time.Time{}, awserr.New("ProviderNotExpirer", 291 fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName), 292 nil) 293 } 294 if c.forceRefresh { 295 // set expiration time to the distant past 296 return time.Time{}, nil 297 } 298 return expirer.ExpiresAt(), nil 299} 300