credentials.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. "fmt"
  52. "github.com/aws/aws-sdk-go/aws/awserr"
  53. "sync"
  54. "time"
  55. )
  56. // AnonymousCredentials is an empty Credential object that can be used as
  57. // dummy placeholder credentials for requests that do not need signed.
  58. //
  59. // This Credentials can be used to configure a service to not sign requests
  60. // when making service API calls. For example, when accessing public
  61. // s3 buckets.
  62. //
  63. // svc := s3.New(session.Must(session.NewSession(&aws.Config{
  64. // Credentials: credentials.AnonymousCredentials,
  65. // })))
  66. // // Access public S3 buckets.
  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 Expirer is an interface that Providers can implement to expose the expiration
  94. // time, if known. If the Provider cannot accurately provide this info,
  95. // it should not implement this interface.
  96. type Expirer interface {
  97. // The time at which the credentials are no longer valid
  98. ExpiresAt() time.Time
  99. }
  100. // An ErrorProvider is a stub credentials provider that always returns an error
  101. // this is used by the SDK when construction a known provider is not possible
  102. // due to an error.
  103. type ErrorProvider struct {
  104. // The error to be returned from Retrieve
  105. Err error
  106. // The provider name to set on the Retrieved returned Value
  107. ProviderName string
  108. }
  109. // Retrieve will always return the error that the ErrorProvider was created with.
  110. func (p ErrorProvider) Retrieve() (Value, error) {
  111. return Value{ProviderName: p.ProviderName}, p.Err
  112. }
  113. // IsExpired will always return not expired.
  114. func (p ErrorProvider) IsExpired() bool {
  115. return false
  116. }
  117. // A Expiry provides shared expiration logic to be used by credentials
  118. // providers to implement expiry functionality.
  119. //
  120. // The best method to use this struct is as an anonymous field within the
  121. // provider's struct.
  122. //
  123. // Example:
  124. // type EC2RoleProvider struct {
  125. // Expiry
  126. // ...
  127. // }
  128. type Expiry struct {
  129. // The date/time when to expire on
  130. expiration time.Time
  131. // If set will be used by IsExpired to determine the current time.
  132. // Defaults to time.Now if CurrentTime is not set. Available for testing
  133. // to be able to mock out the current time.
  134. CurrentTime func() time.Time
  135. }
  136. // SetExpiration sets the expiration IsExpired will check when called.
  137. //
  138. // If window is greater than 0 the expiration time will be reduced by the
  139. // window value.
  140. //
  141. // Using a window is helpful to trigger credentials to expire sooner than
  142. // the expiration time given to ensure no requests are made with expired
  143. // tokens.
  144. func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
  145. e.expiration = expiration
  146. if window > 0 {
  147. e.expiration = e.expiration.Add(-window)
  148. }
  149. }
  150. // IsExpired returns if the credentials are expired.
  151. func (e *Expiry) IsExpired() bool {
  152. curTime := e.CurrentTime
  153. if curTime == nil {
  154. curTime = time.Now
  155. }
  156. return e.expiration.Before(curTime())
  157. }
  158. // ExpiresAt returns the expiration time of the credential
  159. func (e *Expiry) ExpiresAt() time.Time {
  160. return e.expiration
  161. }
  162. // A Credentials provides concurrency safe retrieval of AWS credentials Value.
  163. // Credentials will cache the credentials value until they expire. Once the value
  164. // expires the next Get will attempt to retrieve valid credentials.
  165. //
  166. // Credentials is safe to use across multiple goroutines and will manage the
  167. // synchronous state so the Providers do not need to implement their own
  168. // synchronization.
  169. //
  170. // The first Credentials.Get() will always call Provider.Retrieve() to get the
  171. // first instance of the credentials Value. All calls to Get() after that
  172. // will return the cached credentials Value until IsExpired() returns true.
  173. type Credentials struct {
  174. creds Value
  175. forceRefresh bool
  176. m sync.RWMutex
  177. provider Provider
  178. }
  179. // NewCredentials returns a pointer to a new Credentials with the provider set.
  180. func NewCredentials(provider Provider) *Credentials {
  181. return &Credentials{
  182. provider: provider,
  183. forceRefresh: true,
  184. }
  185. }
  186. // Get returns the credentials value, or error if the credentials Value failed
  187. // to be retrieved.
  188. //
  189. // Will return the cached credentials Value if it has not expired. If the
  190. // credentials Value has expired the Provider's Retrieve() will be called
  191. // to refresh the credentials.
  192. //
  193. // If Credentials.Expire() was called the credentials Value will be force
  194. // expired, and the next call to Get() will cause them to be refreshed.
  195. func (c *Credentials) Get() (Value, error) {
  196. // Check the cached credentials first with just the read lock.
  197. c.m.RLock()
  198. if !c.isExpired() {
  199. creds := c.creds
  200. c.m.RUnlock()
  201. return creds, nil
  202. }
  203. c.m.RUnlock()
  204. // Credentials are expired need to retrieve the credentials taking the full
  205. // lock.
  206. c.m.Lock()
  207. defer c.m.Unlock()
  208. if c.isExpired() {
  209. creds, err := c.provider.Retrieve()
  210. if err != nil {
  211. return Value{}, err
  212. }
  213. c.creds = creds
  214. c.forceRefresh = false
  215. }
  216. return c.creds, nil
  217. }
  218. // Expire expires the credentials and forces them to be retrieved on the
  219. // next call to Get().
  220. //
  221. // This will override the Provider's expired state, and force Credentials
  222. // to call the Provider's Retrieve().
  223. func (c *Credentials) Expire() {
  224. c.m.Lock()
  225. defer c.m.Unlock()
  226. c.forceRefresh = true
  227. }
  228. // IsExpired returns if the credentials are no longer valid, and need
  229. // to be retrieved.
  230. //
  231. // If the Credentials were forced to be expired with Expire() this will
  232. // reflect that override.
  233. func (c *Credentials) IsExpired() bool {
  234. c.m.RLock()
  235. defer c.m.RUnlock()
  236. return c.isExpired()
  237. }
  238. // isExpired helper method wrapping the definition of expired credentials.
  239. func (c *Credentials) isExpired() bool {
  240. return c.forceRefresh || c.provider.IsExpired()
  241. }
  242. // ExpiresAt provides access to the functionality of the Expirer interface of
  243. // the underlying Provider, if it supports that interface. Otherwise, it returns
  244. // an error.
  245. func (c *Credentials) ExpiresAt() (time.Time, error) {
  246. c.m.RLock()
  247. defer c.m.RUnlock()
  248. expirer, ok := c.provider.(Expirer)
  249. if !ok {
  250. return time.Time{}, awserr.New("ProviderNotExpirer",
  251. fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName),
  252. nil)
  253. }
  254. if c.forceRefresh {
  255. // set expiration time to the distant past
  256. return time.Time{}, nil
  257. }
  258. return expirer.ExpiresAt(), nil
  259. }