credentials.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. var AWS = require('./core');
  2. /**
  3. * Represents your AWS security credentials, specifically the
  4. * {accessKeyId}, {secretAccessKey}, and optional {sessionToken}.
  5. * Creating a `Credentials` object allows you to pass around your
  6. * security information to configuration and service objects.
  7. *
  8. * Note that this class typically does not need to be constructed manually,
  9. * as the {AWS.Config} and {AWS.Service} classes both accept simple
  10. * options hashes with the three keys. These structures will be converted
  11. * into Credentials objects automatically.
  12. *
  13. * ## Expiring and Refreshing Credentials
  14. *
  15. * Occasionally credentials can expire in the middle of a long-running
  16. * application. In this case, the SDK will automatically attempt to
  17. * refresh the credentials from the storage location if the Credentials
  18. * class implements the {refresh} method.
  19. *
  20. * If you are implementing a credential storage location, you
  21. * will want to create a subclass of the `Credentials` class and
  22. * override the {refresh} method. This method allows credentials to be
  23. * retrieved from the backing store, be it a file system, database, or
  24. * some network storage. The method should reset the credential attributes
  25. * on the object.
  26. *
  27. * @!attribute expired
  28. * @return [Boolean] whether the credentials have been expired and
  29. * require a refresh. Used in conjunction with {expireTime}.
  30. * @!attribute expireTime
  31. * @return [Date] a time when credentials should be considered expired. Used
  32. * in conjunction with {expired}.
  33. * @!attribute accessKeyId
  34. * @return [String] the AWS access key ID
  35. * @!attribute secretAccessKey
  36. * @return [String] the AWS secret access key
  37. * @!attribute sessionToken
  38. * @return [String] an optional AWS session token
  39. */
  40. AWS.Credentials = AWS.util.inherit({
  41. /**
  42. * A credentials object can be created using positional arguments or an options
  43. * hash.
  44. *
  45. * @overload AWS.Credentials(accessKeyId, secretAccessKey, sessionToken=null)
  46. * Creates a Credentials object with a given set of credential information
  47. * as positional arguments.
  48. * @param accessKeyId [String] the AWS access key ID
  49. * @param secretAccessKey [String] the AWS secret access key
  50. * @param sessionToken [String] the optional AWS session token
  51. * @example Create a credentials object with AWS credentials
  52. * var creds = new AWS.Credentials('akid', 'secret', 'session');
  53. * @overload AWS.Credentials(options)
  54. * Creates a Credentials object with a given set of credential information
  55. * as an options hash.
  56. * @option options accessKeyId [String] the AWS access key ID
  57. * @option options secretAccessKey [String] the AWS secret access key
  58. * @option options sessionToken [String] the optional AWS session token
  59. * @example Create a credentials object with AWS credentials
  60. * var creds = new AWS.Credentials({
  61. * accessKeyId: 'akid', secretAccessKey: 'secret', sessionToken: 'session'
  62. * });
  63. */
  64. constructor: function Credentials() {
  65. // hide secretAccessKey from being displayed with util.inspect
  66. AWS.util.hideProperties(this, ['secretAccessKey']);
  67. this.expired = false;
  68. this.expireTime = null;
  69. this.refreshCallbacks = [];
  70. if (arguments.length === 1 && typeof arguments[0] === 'object') {
  71. var creds = arguments[0].credentials || arguments[0];
  72. this.accessKeyId = creds.accessKeyId;
  73. this.secretAccessKey = creds.secretAccessKey;
  74. this.sessionToken = creds.sessionToken;
  75. } else {
  76. this.accessKeyId = arguments[0];
  77. this.secretAccessKey = arguments[1];
  78. this.sessionToken = arguments[2];
  79. }
  80. },
  81. /**
  82. * @return [Integer] the number of seconds before {expireTime} during which
  83. * the credentials will be considered expired.
  84. */
  85. expiryWindow: 15,
  86. /**
  87. * @return [Boolean] whether the credentials object should call {refresh}
  88. * @note Subclasses should override this method to provide custom refresh
  89. * logic.
  90. */
  91. needsRefresh: function needsRefresh() {
  92. var currentTime = AWS.util.date.getDate().getTime();
  93. var adjustedTime = new Date(currentTime + this.expiryWindow * 1000);
  94. if (this.expireTime && adjustedTime > this.expireTime) {
  95. return true;
  96. } else {
  97. return this.expired || !this.accessKeyId || !this.secretAccessKey;
  98. }
  99. },
  100. /**
  101. * Gets the existing credentials, refreshing them if they are not yet loaded
  102. * or have expired. Users should call this method before using {refresh},
  103. * as this will not attempt to reload credentials when they are already
  104. * loaded into the object.
  105. *
  106. * @callback callback function(err)
  107. * When this callback is called with no error, it means either credentials
  108. * do not need to be refreshed or refreshed credentials information has
  109. * been loaded into the object (as the `accessKeyId`, `secretAccessKey`,
  110. * and `sessionToken` properties).
  111. * @param err [Error] if an error occurred, this value will be filled
  112. */
  113. get: function get(callback) {
  114. var self = this;
  115. if (this.needsRefresh()) {
  116. this.refresh(function(err) {
  117. if (!err) self.expired = false; // reset expired flag
  118. if (callback) callback(err);
  119. });
  120. } else if (callback) {
  121. callback();
  122. }
  123. },
  124. /**
  125. * @!method getPromise()
  126. * Returns a 'thenable' promise.
  127. * Gets the existing credentials, refreshing them if they are not yet loaded
  128. * or have expired. Users should call this method before using {refresh},
  129. * as this will not attempt to reload credentials when they are already
  130. * loaded into the object.
  131. *
  132. * Two callbacks can be provided to the `then` method on the returned promise.
  133. * The first callback will be called if the promise is fulfilled, and the second
  134. * callback will be called if the promise is rejected.
  135. * @callback fulfilledCallback function()
  136. * Called if the promise is fulfilled. When this callback is called, it
  137. * means either credentials do not need to be refreshed or refreshed
  138. * credentials information has been loaded into the object (as the
  139. * `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
  140. * @callback rejectedCallback function(err)
  141. * Called if the promise is rejected.
  142. * @param err [Error] if an error occurred, this value will be filled
  143. * @return [Promise] A promise that represents the state of the `get` call.
  144. * @example Calling the `getPromise` method.
  145. * var promise = credProvider.getPromise();
  146. * promise.then(function() { ... }, function(err) { ... });
  147. */
  148. /**
  149. * @!method refreshPromise()
  150. * Returns a 'thenable' promise.
  151. * Refreshes the credentials. Users should call {get} before attempting
  152. * to forcibly refresh credentials.
  153. *
  154. * Two callbacks can be provided to the `then` method on the returned promise.
  155. * The first callback will be called if the promise is fulfilled, and the second
  156. * callback will be called if the promise is rejected.
  157. * @callback fulfilledCallback function()
  158. * Called if the promise is fulfilled. When this callback is called, it
  159. * means refreshed credentials information has been loaded into the object
  160. * (as the `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
  161. * @callback rejectedCallback function(err)
  162. * Called if the promise is rejected.
  163. * @param err [Error] if an error occurred, this value will be filled
  164. * @return [Promise] A promise that represents the state of the `refresh` call.
  165. * @example Calling the `refreshPromise` method.
  166. * var promise = credProvider.refreshPromise();
  167. * promise.then(function() { ... }, function(err) { ... });
  168. */
  169. /**
  170. * Refreshes the credentials. Users should call {get} before attempting
  171. * to forcibly refresh credentials.
  172. *
  173. * @callback callback function(err)
  174. * When this callback is called with no error, it means refreshed
  175. * credentials information has been loaded into the object (as the
  176. * `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
  177. * @param err [Error] if an error occurred, this value will be filled
  178. * @note Subclasses should override this class to reset the
  179. * {accessKeyId}, {secretAccessKey} and optional {sessionToken}
  180. * on the credentials object and then call the callback with
  181. * any error information.
  182. * @see get
  183. */
  184. refresh: function refresh(callback) {
  185. this.expired = false;
  186. callback();
  187. },
  188. /**
  189. * @api private
  190. * @param callback
  191. */
  192. coalesceRefresh: function coalesceRefresh(callback, sync) {
  193. var self = this;
  194. if (self.refreshCallbacks.push(callback) === 1) {
  195. self.load(function onLoad(err) {
  196. AWS.util.arrayEach(self.refreshCallbacks, function(callback) {
  197. if (sync) {
  198. callback(err);
  199. } else {
  200. // callback could throw, so defer to ensure all callbacks are notified
  201. AWS.util.defer(function () {
  202. callback(err);
  203. });
  204. }
  205. });
  206. self.refreshCallbacks.length = 0;
  207. });
  208. }
  209. },
  210. /**
  211. * @api private
  212. * @param callback
  213. */
  214. load: function load(callback) {
  215. callback();
  216. }
  217. });
  218. /**
  219. * @api private
  220. */
  221. AWS.Credentials.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
  222. this.prototype.getPromise = AWS.util.promisifyMethod('get', PromiseDependency);
  223. this.prototype.refreshPromise = AWS.util.promisifyMethod('refresh', PromiseDependency);
  224. };
  225. /**
  226. * @api private
  227. */
  228. AWS.Credentials.deletePromisesFromClass = function deletePromisesFromClass() {
  229. delete this.prototype.getPromise;
  230. delete this.prototype.refreshPromise;
  231. };
  232. AWS.util.addPromises(AWS.Credentials);