UNPKG

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