credentials.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. //
  49. package credentials
  50. import (
  51. "sync"
  52. "time"
  53. )
  54. // AnonymousCredentials is an empty Credential object that can be used as
  55. // dummy placeholder credentials for requests that do not need signed.
  56. //
  57. // This Credentials can be used to configure a service to not sign requests
  58. // when making service API calls. For example, when accessing public
  59. // s3 buckets.
  60. //
  61. // svc := s3.New(session.Must(session.NewSession(&aws.Config{
  62. // Credentials: credentials.AnonymousCredentials,
  63. // })))
  64. // // Access public S3 buckets.
  65. //
  66. // @readonly
  67. var AnonymousCredentials = NewStaticCredentials("", "", "")
  68. // A Value is the AWS credentials value for individual credential fields.
  69. type Value struct {
  70. // AWS Access key ID
  71. AccessKeyID string
  72. // AWS Secret Access Key
  73. SecretAccessKey string
  74. // AWS Session Token
  75. SessionToken string
  76. // Provider used to get credentials
  77. ProviderName string
  78. }
  79. // A Provider is the interface for any component which will provide credentials
  80. // Value. A provider is required to manage its own Expired state, and what to
  81. // be expired means.
  82. //
  83. // The Provider should not need to implement its own mutexes, because
  84. // that will be managed by Credentials.
  85. type Provider interface {
  86. // Retrieve returns nil if it successfully retrieved the value.
  87. // Error is returned if the value were not obtainable, or empty.
  88. Retrieve() (Value, error)
  89. // IsExpired returns if the credentials are no longer valid, and need
  90. // to be retrieved.
  91. IsExpired() bool
  92. }
  93. // An ErrorProvider is a stub credentials provider that always returns an error
  94. // this is used by the SDK when construction a known provider is not possible
  95. // due to an error.
  96. type ErrorProvider struct {
  97. // The error to be returned from Retrieve
  98. Err error
  99. // The provider name to set on the Retrieved returned Value
  100. ProviderName string
  101. }
  102. // Retrieve will always return the error that the ErrorProvider was created with.
  103. func (p ErrorProvider) Retrieve() (Value, error) {
  104. return Value{ProviderName: p.ProviderName}, p.Err
  105. }
  106. // IsExpired will always return not expired.
  107. func (p ErrorProvider) IsExpired() bool {
  108. return false
  109. }
  110. // A Expiry provides shared expiration logic to be used by credentials
  111. // providers to implement expiry functionality.
  112. //
  113. // The best method to use this struct is as an anonymous field within the
  114. // provider's struct.
  115. //
  116. // Example:
  117. // type EC2RoleProvider struct {
  118. // Expiry
  119. // ...
  120. // }
  121. type Expiry struct {
  122. // The date/time when to expire on
  123. expiration time.Time
  124. // If set will be used by IsExpired to determine the current time.
  125. // Defaults to time.Now if CurrentTime is not set. Available for testing
  126. // to be able to mock out the current time.
  127. CurrentTime func() time.Time
  128. }
  129. // SetExpiration sets the expiration IsExpired will check when called.
  130. //
  131. // If window is greater than 0 the expiration time will be reduced by the
  132. // window value.
  133. //
  134. // Using a window is helpful to trigger credentials to expire sooner than
  135. // the expiration time given to ensure no requests are made with expired
  136. // tokens.
  137. func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
  138. e.expiration = expiration
  139. if window > 0 {
  140. e.expiration = e.expiration.Add(-window)
  141. }
  142. }
  143. // IsExpired returns if the credentials are expired.
  144. func (e *Expiry) IsExpired() bool {
  145. if e.CurrentTime == nil {
  146. e.CurrentTime = time.Now
  147. }
  148. return e.expiration.Before(e.CurrentTime())
  149. }
  150. // A Credentials provides synchronous safe retrieval of AWS credentials Value.
  151. // Credentials will cache the credentials value until they expire. Once the value
  152. // expires the next Get will attempt to retrieve valid credentials.
  153. //
  154. // Credentials is safe to use across multiple goroutines and will manage the
  155. // synchronous state so the Providers do not need to implement their own
  156. // synchronization.
  157. //
  158. // The first Credentials.Get() will always call Provider.Retrieve() to get the
  159. // first instance of the credentials Value. All calls to Get() after that
  160. // will return the cached credentials Value until IsExpired() returns true.
  161. type Credentials struct {
  162. creds Value
  163. forceRefresh bool
  164. m sync.Mutex
  165. provider Provider
  166. }
  167. // NewCredentials returns a pointer to a new Credentials with the provider set.
  168. func NewCredentials(provider Provider) *Credentials {
  169. return &Credentials{
  170. provider: provider,
  171. forceRefresh: true,
  172. }
  173. }
  174. // Get returns the credentials value, or error if the credentials Value failed
  175. // to be retrieved.
  176. //
  177. // Will return the cached credentials Value if it has not expired. If the
  178. // credentials Value has expired the Provider's Retrieve() will be called
  179. // to refresh the credentials.
  180. //
  181. // If Credentials.Expire() was called the credentials Value will be force
  182. // expired, and the next call to Get() will cause them to be refreshed.
  183. func (c *Credentials) Get() (Value, error) {
  184. c.m.Lock()
  185. defer c.m.Unlock()
  186. if c.isExpired() {
  187. creds, err := c.provider.Retrieve()
  188. if err != nil {
  189. return Value{}, err
  190. }
  191. c.creds = creds
  192. c.forceRefresh = false
  193. }
  194. return c.creds, nil
  195. }
  196. // Expire expires the credentials and forces them to be retrieved on the
  197. // next call to Get().
  198. //
  199. // This will override the Provider's expired state, and force Credentials
  200. // to call the Provider's Retrieve().
  201. func (c *Credentials) Expire() {
  202. c.m.Lock()
  203. defer c.m.Unlock()
  204. c.forceRefresh = true
  205. }
  206. // IsExpired returns if the credentials are no longer valid, and need
  207. // to be retrieved.
  208. //
  209. // If the Credentials were forced to be expired with Expire() this will
  210. // reflect that override.
  211. func (c *Credentials) IsExpired() bool {
  212. c.m.Lock()
  213. defer c.m.Unlock()
  214. return c.isExpired()
  215. }
  216. // isExpired helper method wrapping the definition of expired credentials.
  217. func (c *Credentials) isExpired() bool {
  218. return c.forceRefresh || c.provider.IsExpired()
  219. }