| 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 |