OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Handles web page requests for gnubby sign requests. | 6 * @fileoverview Handles web page requests for gnubby sign requests. |
7 * | 7 * |
8 */ | 8 */ |
9 | 9 |
10 'use strict'; | 10 'use strict'; |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 } | 137 } |
138 // More closure type inference fail. | 138 // More closure type inference fail. |
139 var nonNullOrigin = /** @type {string} */ (origin); | 139 var nonNullOrigin = /** @type {string} */ (origin); |
140 | 140 |
141 if (!isValidSignRequest(request, signChallengesName)) { | 141 if (!isValidSignRequest(request, signChallengesName)) { |
142 errorCb(ErrorCodes.BAD_REQUEST); | 142 errorCb(ErrorCodes.BAD_REQUEST); |
143 return null; | 143 return null; |
144 } | 144 } |
145 | 145 |
146 var signChallenges = request[signChallengesName]; | 146 var signChallenges = request[signChallengesName]; |
147 // A valid sign data has at least one challenge, so get the first appId from | 147 var appId; |
148 // the first challenge. | 148 if (request['appId']) { |
149 var firstAppId = signChallenges[0]['appId']; | 149 appId = request['appId']; |
| 150 } else { |
| 151 // A valid sign data has at least one challenge, so get the appId from |
| 152 // the first challenge. |
| 153 appId = signChallenges[0]['appId']; |
| 154 } |
| 155 // Sanity check |
| 156 if (!appId) { |
| 157 console.warn(UTIL_fmt('empty sign appId?')); |
| 158 errorCb(ErrorCodes.BAD_REQUEST); |
| 159 return null; |
| 160 } |
150 var timer = createTimerForRequest( | 161 var timer = createTimerForRequest( |
151 FACTORY_REGISTRY.getCountdownFactory(), request); | 162 FACTORY_REGISTRY.getCountdownFactory(), request); |
152 var logMsgUrl = request['logMsgUrl']; | 163 var logMsgUrl = request['logMsgUrl']; |
153 | 164 |
154 // Queue sign requests from the same origin, to protect against simultaneous | 165 // Queue sign requests from the same origin, to protect against simultaneous |
155 // sign-out on many tabs resulting in repeated sign-in requests. | 166 // sign-out on many tabs resulting in repeated sign-in requests. |
156 var queuedSignRequest = new QueuedSignRequest(signChallenges, | 167 var queuedSignRequest = new QueuedSignRequest(signChallenges, |
157 timer, nonNullOrigin, errorCb, successCb, sender.tlsChannelId, | 168 timer, nonNullOrigin, errorCb, successCb, appId, sender.tlsChannelId, |
158 logMsgUrl); | 169 logMsgUrl); |
159 var requestToken = signRequestQueue.queueRequest(firstAppId, nonNullOrigin, | 170 var requestToken = signRequestQueue.queueRequest(appId, nonNullOrigin, |
160 queuedSignRequest.begin.bind(queuedSignRequest), timer); | 171 queuedSignRequest.begin.bind(queuedSignRequest), timer); |
161 queuedSignRequest.setToken(requestToken); | 172 queuedSignRequest.setToken(requestToken); |
162 return queuedSignRequest; | 173 return queuedSignRequest; |
163 } | 174 } |
164 | 175 |
165 /** | 176 /** |
166 * Returns whether the request appears to be a valid sign request. | 177 * Returns whether the request appears to be a valid sign request. |
167 * @param {Object} request The request. | 178 * @param {Object} request The request. |
168 * @param {string} signChallengesName The name of the sign challenges value in | 179 * @param {string} signChallengesName The name of the sign challenges value in |
169 * the request. | 180 * the request. |
170 * @return {boolean} Whether the request appears valid. | 181 * @return {boolean} Whether the request appears valid. |
171 */ | 182 */ |
172 function isValidSignRequest(request, signChallengesName) { | 183 function isValidSignRequest(request, signChallengesName) { |
173 if (!request.hasOwnProperty(signChallengesName)) | 184 if (!request.hasOwnProperty(signChallengesName)) |
174 return false; | 185 return false; |
175 var signChallenges = request[signChallengesName]; | 186 var signChallenges = request[signChallengesName]; |
176 // If a sign request contains an empty array of challenges, it could never | 187 // If a sign request contains an empty array of challenges, it could never |
177 // be fulfilled. Fail. | 188 // be fulfilled. Fail. |
178 if (!signChallenges.length) | 189 if (!signChallenges.length) |
179 return false; | 190 return false; |
180 return isValidSignChallengeArray(signChallenges); | 191 var hasAppId = request.hasOwnProperty('appId'); |
| 192 return isValidSignChallengeArray(signChallenges, !hasAppId); |
181 } | 193 } |
182 | 194 |
183 /** | 195 /** |
184 * Adapter class representing a queued sign request. | 196 * Adapter class representing a queued sign request. |
185 * @param {!Array.<SignChallenge>} signChallenges The sign challenges. | 197 * @param {!Array.<SignChallenge>} signChallenges The sign challenges. |
186 * @param {Countdown} timer Timeout timer | 198 * @param {Countdown} timer Timeout timer |
187 * @param {string} origin Signature origin | 199 * @param {string} origin Signature origin |
188 * @param {function(ErrorCodes)} errorCb Error callback | 200 * @param {function(ErrorCodes)} errorCb Error callback |
189 * @param {function(SignChallenge, string, string)} successCb Success callback | 201 * @param {function(SignChallenge, string, string)} successCb Success callback |
| 202 * @param {string|undefined} opt_appId The app id for the entire request. |
190 * @param {string|undefined} opt_tlsChannelId TLS Channel Id | 203 * @param {string|undefined} opt_tlsChannelId TLS Channel Id |
191 * @param {string|undefined} opt_logMsgUrl Url to post log messages to | 204 * @param {string|undefined} opt_logMsgUrl Url to post log messages to |
192 * @constructor | 205 * @constructor |
193 * @implements {Closeable} | 206 * @implements {Closeable} |
194 */ | 207 */ |
195 function QueuedSignRequest(signChallenges, timer, origin, errorCb, | 208 function QueuedSignRequest(signChallenges, timer, origin, errorCb, |
196 successCb, opt_tlsChannelId, opt_logMsgUrl) { | 209 successCb, opt_appId, opt_tlsChannelId, opt_logMsgUrl) { |
197 /** @private {!Array.<SignChallenge>} */ | 210 /** @private {!Array.<SignChallenge>} */ |
198 this.signChallenges_ = signChallenges; | 211 this.signChallenges_ = signChallenges; |
199 /** @private {Countdown} */ | 212 /** @private {Countdown} */ |
200 this.timer_ = timer; | 213 this.timer_ = timer; |
201 /** @private {string} */ | 214 /** @private {string} */ |
202 this.origin_ = origin; | 215 this.origin_ = origin; |
203 /** @private {function(ErrorCodes)} */ | 216 /** @private {function(ErrorCodes)} */ |
204 this.errorCb_ = errorCb; | 217 this.errorCb_ = errorCb; |
205 /** @private {function(SignChallenge, string, string)} */ | 218 /** @private {function(SignChallenge, string, string)} */ |
206 this.successCb_ = successCb; | 219 this.successCb_ = successCb; |
207 /** @private {string|undefined} */ | 220 /** @private {string|undefined} */ |
| 221 this.appId_ = opt_appId; |
| 222 /** @private {string|undefined} */ |
208 this.tlsChannelId_ = opt_tlsChannelId; | 223 this.tlsChannelId_ = opt_tlsChannelId; |
209 /** @private {string|undefined} */ | 224 /** @private {string|undefined} */ |
210 this.logMsgUrl_ = opt_logMsgUrl; | 225 this.logMsgUrl_ = opt_logMsgUrl; |
211 /** @private {boolean} */ | 226 /** @private {boolean} */ |
212 this.begun_ = false; | 227 this.begun_ = false; |
213 /** @private {boolean} */ | 228 /** @private {boolean} */ |
214 this.closed_ = false; | 229 this.closed_ = false; |
215 } | 230 } |
216 | 231 |
217 /** Closes this sign request. */ | 232 /** Closes this sign request. */ |
(...skipping 19 matching lines...) Expand all Loading... |
237 /** | 252 /** |
238 * Called when this sign request may begin work. | 253 * Called when this sign request may begin work. |
239 * @param {QueuedRequestToken} token Token for this sign request. | 254 * @param {QueuedRequestToken} token Token for this sign request. |
240 */ | 255 */ |
241 QueuedSignRequest.prototype.begin = function(token) { | 256 QueuedSignRequest.prototype.begin = function(token) { |
242 this.begun_ = true; | 257 this.begun_ = true; |
243 this.setToken(token); | 258 this.setToken(token); |
244 this.signer_ = new Signer(this.timer_, this.origin_, | 259 this.signer_ = new Signer(this.timer_, this.origin_, |
245 this.signerFailed_.bind(this), this.signerSucceeded_.bind(this), | 260 this.signerFailed_.bind(this), this.signerSucceeded_.bind(this), |
246 this.tlsChannelId_, this.logMsgUrl_); | 261 this.tlsChannelId_, this.logMsgUrl_); |
247 if (!this.signer_.setChallenges(this.signChallenges_)) { | 262 if (!this.signer_.setChallenges(this.signChallenges_, this.appId_)) { |
248 token.complete(); | 263 token.complete(); |
249 this.errorCb_(ErrorCodes.BAD_REQUEST); | 264 this.errorCb_(ErrorCodes.BAD_REQUEST); |
250 } | 265 } |
251 }; | 266 }; |
252 | 267 |
253 /** | 268 /** |
254 * Called when this request's signer fails. | 269 * Called when this request's signer fails. |
255 * @param {ErrorCodes} code The failure code reported by the signer. | 270 * @param {ErrorCodes} code The failure code reported by the signer. |
256 * @private | 271 * @private |
257 */ | 272 */ |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 // what they get.) | 328 // what they get.) |
314 /** @private {boolean} */ | 329 /** @private {boolean} */ |
315 this.allowHttp_ = this.origin_ ? this.origin_.indexOf('http://') == 0 : false; | 330 this.allowHttp_ = this.origin_ ? this.origin_.indexOf('http://') == 0 : false; |
316 /** @private {Closeable} */ | 331 /** @private {Closeable} */ |
317 this.handler_ = null; | 332 this.handler_ = null; |
318 } | 333 } |
319 | 334 |
320 /** | 335 /** |
321 * Sets the challenges to be signed. | 336 * Sets the challenges to be signed. |
322 * @param {Array.<SignChallenge>} signChallenges The challenges to set. | 337 * @param {Array.<SignChallenge>} signChallenges The challenges to set. |
| 338 * @param {string=} opt_appId The app id for the entire request. |
323 * @return {boolean} Whether the challenges could be set. | 339 * @return {boolean} Whether the challenges could be set. |
324 */ | 340 */ |
325 Signer.prototype.setChallenges = function(signChallenges) { | 341 Signer.prototype.setChallenges = function(signChallenges, opt_appId) { |
326 if (this.challengesSet_ || this.done_) | 342 if (this.challengesSet_ || this.done_) |
327 return false; | 343 return false; |
328 /** @private {Array.<SignChallenge>} */ | 344 /** @private {Array.<SignChallenge>} */ |
329 this.signChallenges_ = signChallenges; | 345 this.signChallenges_ = signChallenges; |
| 346 /** @private {string|undefined} */ |
| 347 this.appId_ = opt_appId; |
330 /** @private {boolean} */ | 348 /** @private {boolean} */ |
331 this.challengesSet_ = true; | 349 this.challengesSet_ = true; |
332 | 350 |
333 this.checkAppIds_(); | 351 this.checkAppIds_(); |
334 return true; | 352 return true; |
335 }; | 353 }; |
336 | 354 |
337 /** | 355 /** |
338 * Checks the app ids of incoming requests. | 356 * Checks the app ids of incoming requests. |
339 * @private | 357 * @private |
340 */ | 358 */ |
341 Signer.prototype.checkAppIds_ = function() { | 359 Signer.prototype.checkAppIds_ = function() { |
342 var appIds = getDistinctAppIds(this.signChallenges_); | 360 var appIds = getDistinctAppIds(this.signChallenges_); |
| 361 if (this.appId_) { |
| 362 appIds = UTIL_unionArrays([this.appId_], appIds); |
| 363 } |
343 if (!appIds || !appIds.length) { | 364 if (!appIds || !appIds.length) { |
344 this.notifyError_(ErrorCodes.BAD_REQUEST); | 365 this.notifyError_(ErrorCodes.BAD_REQUEST); |
345 return; | 366 return; |
346 } | 367 } |
347 FACTORY_REGISTRY.getOriginChecker().canClaimAppIds(this.origin_, appIds) | 368 FACTORY_REGISTRY.getOriginChecker().canClaimAppIds(this.origin_, appIds) |
348 .then(this.originChecked_.bind(this, appIds)); | 369 .then(this.originChecked_.bind(this, appIds)); |
349 }; | 370 }; |
350 | 371 |
351 /** | 372 /** |
352 * Called with the result of checking the origin. When the origin is allowed | 373 * Called with the result of checking the origin. When the origin is allowed |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 var serverChallenge = challenge['challenge']; | 419 var serverChallenge = challenge['challenge']; |
399 var keyHandle = challenge['keyHandle']; | 420 var keyHandle = challenge['keyHandle']; |
400 | 421 |
401 var browserData = | 422 var browserData = |
402 makeSignBrowserData(serverChallenge, this.origin_, this.tlsChannelId_); | 423 makeSignBrowserData(serverChallenge, this.origin_, this.tlsChannelId_); |
403 this.browserData_[keyHandle] = browserData; | 424 this.browserData_[keyHandle] = browserData; |
404 this.serverChallenges_[keyHandle] = challenge; | 425 this.serverChallenges_[keyHandle] = challenge; |
405 } | 426 } |
406 | 427 |
407 var encodedChallenges = encodeSignChallenges(this.signChallenges_, | 428 var encodedChallenges = encodeSignChallenges(this.signChallenges_, |
408 this.getChallengeHash_.bind(this)); | 429 this.appId_, this.getChallengeHash_.bind(this)); |
409 | 430 |
410 var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0; | 431 var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0; |
411 var request = makeSignHelperRequest(encodedChallenges, timeoutSeconds, | 432 var request = makeSignHelperRequest(encodedChallenges, timeoutSeconds, |
412 this.logMsgUrl_); | 433 this.logMsgUrl_); |
413 this.handler_ = | 434 this.handler_ = |
414 FACTORY_REGISTRY.getRequestHelper() | 435 FACTORY_REGISTRY.getRequestHelper() |
415 .getHandler(/** @type {HelperRequest} */ (request)); | 436 .getHandler(/** @type {HelperRequest} */ (request)); |
416 if (!this.handler_) | 437 if (!this.handler_) |
417 return false; | 438 return false; |
418 return this.handler_.run(this.helperComplete_.bind(this)); | 439 return this.handler_.run(this.helperComplete_.bind(this)); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 | 516 |
496 var key = reply.responseData['keyHandle']; | 517 var key = reply.responseData['keyHandle']; |
497 var browserData = this.browserData_[key]; | 518 var browserData = this.browserData_[key]; |
498 // Notify with server-provided challenge, not the encoded one: the | 519 // Notify with server-provided challenge, not the encoded one: the |
499 // server-provided challenge contains additional fields it relies on. | 520 // server-provided challenge contains additional fields it relies on. |
500 var serverChallenge = this.serverChallenges_[key]; | 521 var serverChallenge = this.serverChallenges_[key]; |
501 this.notifySuccess_(serverChallenge, reply.responseData.signatureData, | 522 this.notifySuccess_(serverChallenge, reply.responseData.signatureData, |
502 browserData); | 523 browserData); |
503 } | 524 } |
504 }; | 525 }; |
OLD | NEW |