| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 | 6 * @fileoverview |
| 7 * A background script of the auth extension that bridges the communication | 7 * A background script of the auth extension that bridges the communication |
| 8 * between the main and injected scripts. | 8 * between the main and injected scripts. |
| 9 * | 9 * |
| 10 * Here is an overview of the communication flow when SAML is being used: | 10 * Here is an overview of the communication flow when SAML is being used: |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 /** | 26 /** |
| 27 * BackgroundBridgeManager maintains an array of BackgroundBridge, indexed by | 27 * BackgroundBridgeManager maintains an array of BackgroundBridge, indexed by |
| 28 * the associated tab id. | 28 * the associated tab id. |
| 29 */ | 29 */ |
| 30 function BackgroundBridgeManager() { | 30 function BackgroundBridgeManager() { |
| 31 this.bridges_ = {}; | 31 this.bridges_ = {}; |
| 32 } | 32 } |
| 33 | 33 |
| 34 BackgroundBridgeManager.prototype = { | 34 BackgroundBridgeManager.prototype = { |
| 35 CONTINUE_URL_BASE: 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik' + | 35 CONTINUE_URL_BASE: 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik' + |
| 36 '/success.html', | 36 '/success.html', |
| 37 // Maps a tab id to its associated BackgroundBridge. | 37 // Maps a tab id to its associated BackgroundBridge. |
| 38 bridges_: null, | 38 bridges_: null, |
| 39 | 39 |
| 40 run: function() { | 40 run: function() { |
| 41 chrome.runtime.onConnect.addListener(this.onConnect_.bind(this)); | 41 chrome.runtime.onConnect.addListener(this.onConnect_.bind(this)); |
| 42 | 42 |
| 43 chrome.webRequest.onBeforeRequest.addListener( | 43 chrome.webRequest.onBeforeRequest.addListener( |
| 44 function(details) { | 44 function(details) { |
| 45 if (this.bridges_[details.tabId]) | 45 if (this.bridges_[details.tabId]) |
| 46 return this.bridges_[details.tabId].onInsecureRequest(details.url); | 46 return this.bridges_[details.tabId].onInsecureRequest(details.url); |
| 47 }.bind(this), | 47 }.bind(this), |
| 48 {urls: ['http://*/*', 'file://*/*', 'ftp://*/*']}, | 48 {urls: ['http://*/*', 'file://*/*', 'ftp://*/*']}, ['blocking']); |
| 49 ['blocking']); | |
| 50 | 49 |
| 51 chrome.webRequest.onBeforeSendHeaders.addListener( | 50 chrome.webRequest.onBeforeSendHeaders.addListener( |
| 52 function(details) { | 51 function(details) { |
| 53 if (this.bridges_[details.tabId]) | 52 if (this.bridges_[details.tabId]) |
| 54 return this.bridges_[details.tabId].onBeforeSendHeaders(details); | 53 return this.bridges_[details.tabId].onBeforeSendHeaders(details); |
| 55 else | 54 else |
| 56 return {requestHeaders: details.requestHeaders}; | 55 return {requestHeaders: details.requestHeaders}; |
| 57 }.bind(this), | 56 }.bind(this), |
| 58 {urls: ['*://*/*'], types: ['sub_frame']}, | 57 {urls: ['*://*/*'], types: ['sub_frame']}, |
| 59 ['blocking', 'requestHeaders']); | 58 ['blocking', 'requestHeaders']); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 // Registers for SAML related messages. | 168 // Registers for SAML related messages. |
| 170 this.channelMain_.registerMessage( | 169 this.channelMain_.registerMessage( |
| 171 'setGaiaUrl', this.onSetGaiaUrl_.bind(this)); | 170 'setGaiaUrl', this.onSetGaiaUrl_.bind(this)); |
| 172 this.channelMain_.registerMessage( | 171 this.channelMain_.registerMessage( |
| 173 'setBlockInsecureContent', this.onSetBlockInsecureContent_.bind(this)); | 172 'setBlockInsecureContent', this.onSetBlockInsecureContent_.bind(this)); |
| 174 this.channelMain_.registerMessage( | 173 this.channelMain_.registerMessage( |
| 175 'resetAuth', this.onResetAuth_.bind(this)); | 174 'resetAuth', this.onResetAuth_.bind(this)); |
| 176 this.channelMain_.registerMessage( | 175 this.channelMain_.registerMessage( |
| 177 'startAuth', this.onAuthStarted_.bind(this)); | 176 'startAuth', this.onAuthStarted_.bind(this)); |
| 178 this.channelMain_.registerMessage( | 177 this.channelMain_.registerMessage( |
| 179 'getScrapedPasswords', | 178 'getScrapedPasswords', this.onGetScrapedPasswords_.bind(this)); |
| 180 this.onGetScrapedPasswords_.bind(this)); | |
| 181 this.channelMain_.registerMessage( | 179 this.channelMain_.registerMessage( |
| 182 'apiResponse', this.onAPIResponse_.bind(this)); | 180 'apiResponse', this.onAPIResponse_.bind(this)); |
| 183 | 181 |
| 184 this.channelMain_.send({ | 182 this.channelMain_.send({'name': 'channelConnected'}); |
| 185 'name': 'channelConnected' | |
| 186 }); | |
| 187 }, | 183 }, |
| 188 | 184 |
| 189 /** | 185 /** |
| 190 * Sets up the communication channel with the injected script. | 186 * Sets up the communication channel with the injected script. |
| 191 */ | 187 */ |
| 192 setupForInjected: function(port) { | 188 setupForInjected: function(port) { |
| 193 this.channelInjected_ = new Channel(); | 189 this.channelInjected_ = new Channel(); |
| 194 this.channelInjected_.init(port); | 190 this.channelInjected_.init(port); |
| 195 | 191 |
| 196 this.channelInjected_.registerMessage( | 192 this.channelInjected_.registerMessage( |
| (...skipping 19 matching lines...) Expand all Loading... |
| 216 | 212 |
| 217 /** | 213 /** |
| 218 * Handler for webRequest.onCompleted. It 1) detects loading of continue URL | 214 * Handler for webRequest.onCompleted. It 1) detects loading of continue URL |
| 219 * and notifies the main script of signin completion; 2) detects if the | 215 * and notifies the main script of signin completion; 2) detects if the |
| 220 * current page could be loaded in a constrained window and signals the main | 216 * current page could be loaded in a constrained window and signals the main |
| 221 * script of switching to full tab if necessary. | 217 * script of switching to full tab if necessary. |
| 222 */ | 218 */ |
| 223 onCompleted: function(details) { | 219 onCompleted: function(details) { |
| 224 // Only monitors requests in the gaia frame. The gaia frame is the one | 220 // Only monitors requests in the gaia frame. The gaia frame is the one |
| 225 // where the initial frame URL completes. | 221 // where the initial frame URL completes. |
| 226 if (details.url.lastIndexOf( | 222 if (details.url.lastIndexOf(this.initialFrameUrlWithoutParams, 0) == 0) { |
| 227 this.initialFrameUrlWithoutParams, 0) == 0) { | |
| 228 this.frameId = details.frameId; | 223 this.frameId = details.frameId; |
| 229 } | 224 } |
| 230 if (this.frameId == -1) { | 225 if (this.frameId == -1) { |
| 231 // If for some reason the frameId could not be set above, just make sure | 226 // If for some reason the frameId could not be set above, just make sure |
| 232 // the frame is more than two levels deep (since the gaia frame is at | 227 // the frame is more than two levels deep (since the gaia frame is at |
| 233 // least three levels deep). | 228 // least three levels deep). |
| 234 if (details.parentFrameId <= 0) | 229 if (details.parentFrameId <= 0) |
| 235 return; | 230 return; |
| 236 } else if (details.frameId != this.frameId) { | 231 } else if (details.frameId != this.frameId) { |
| 237 return; | 232 return; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 256 this.channelMain_.send(msg); | 251 this.channelMain_.send(msg); |
| 257 } else if (this.isConstrainedWindow_) { | 252 } else if (this.isConstrainedWindow_) { |
| 258 // The header google-accounts-embedded is only set on gaia domain. | 253 // The header google-accounts-embedded is only set on gaia domain. |
| 259 if (this.gaiaUrl_ && details.url.lastIndexOf(this.gaiaUrl_) == 0) { | 254 if (this.gaiaUrl_ && details.url.lastIndexOf(this.gaiaUrl_) == 0) { |
| 260 var headers = details.responseHeaders; | 255 var headers = details.responseHeaders; |
| 261 for (var i = 0; headers && i < headers.length; ++i) { | 256 for (var i = 0; headers && i < headers.length; ++i) { |
| 262 if (headers[i].name.toLowerCase() == 'google-accounts-embedded') | 257 if (headers[i].name.toLowerCase() == 'google-accounts-embedded') |
| 263 return; | 258 return; |
| 264 } | 259 } |
| 265 } | 260 } |
| 266 var msg = { | 261 var msg = {'name': 'switchToFullTab', 'url': details.url}; |
| 267 'name': 'switchToFullTab', | |
| 268 'url': details.url | |
| 269 }; | |
| 270 this.channelMain_.send(msg); | 262 this.channelMain_.send(msg); |
| 271 } | 263 } |
| 272 }, | 264 }, |
| 273 | 265 |
| 274 /** | 266 /** |
| 275 * Handler for webRequest.onBeforeRequest, invoked when content served over an | 267 * Handler for webRequest.onBeforeRequest, invoked when content served over an |
| 276 * unencrypted connection is detected. Determines whether the request should | 268 * unencrypted connection is detected. Determines whether the request should |
| 277 * be blocked and if so, signals that an error message needs to be shown. | 269 * be blocked and if so, signals that an error message needs to be shown. |
| 278 * @param {string} url The URL that was blocked. | 270 * @param {string} url The URL that was blocked. |
| 279 * @return {!Object} Decision whether to block the request. | 271 * @return {!Object} Decision whether to block the request. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 for (var i = 0; headers && i < headers.length; ++i) { | 309 for (var i = 0; headers && i < headers.length; ++i) { |
| 318 if (headers[i].name.toLowerCase() == 'google-accounts-saml') { | 310 if (headers[i].name.toLowerCase() == 'google-accounts-saml') { |
| 319 var action = headers[i].value.toLowerCase(); | 311 var action = headers[i].value.toLowerCase(); |
| 320 if (action == 'start') { | 312 if (action == 'start') { |
| 321 this.isSAML_ = true; | 313 this.isSAML_ = true; |
| 322 // GAIA is redirecting to a SAML IdP. Any cookies contained in the | 314 // GAIA is redirecting to a SAML IdP. Any cookies contained in the |
| 323 // current |headers| were set by GAIA. Any cookies set in future | 315 // current |headers| were set by GAIA. Any cookies set in future |
| 324 // requests will be coming from the IdP. Append a cookie to the | 316 // requests will be coming from the IdP. Append a cookie to the |
| 325 // current |headers| that marks the point at which the redirect | 317 // current |headers| that marks the point at which the redirect |
| 326 // occurred. | 318 // occurred. |
| 327 headers.push({name: 'Set-Cookie', | 319 headers.push( |
| 328 value: 'google-accounts-saml-start=now'}); | 320 {name: 'Set-Cookie', value: 'google-accounts-saml-start=now'}); |
| 329 return {responseHeaders: headers}; | 321 return {responseHeaders: headers}; |
| 330 } else if (action == 'end') { | 322 } else if (action == 'end') { |
| 331 this.isSAML_ = false; | 323 this.isSAML_ = false; |
| 332 // The SAML IdP has redirected back to GAIA. Add a cookie that marks | 324 // The SAML IdP has redirected back to GAIA. Add a cookie that marks |
| 333 // the point at which the redirect occurred occurred. It is | 325 // the point at which the redirect occurred occurred. It is |
| 334 // important that this cookie be prepended to the current |headers| | 326 // important that this cookie be prepended to the current |headers| |
| 335 // because any cookies contained in the |headers| were already set | 327 // because any cookies contained in the |headers| were already set |
| 336 // by GAIA, not the IdP. Due to limitations in the webRequest API, | 328 // by GAIA, not the IdP. Due to limitations in the webRequest API, |
| 337 // it is not trivial to prepend a cookie: | 329 // it is not trivial to prepend a cookie: |
| 338 // | 330 // |
| 339 // The webRequest API only allows for deleting and appending | 331 // The webRequest API only allows for deleting and appending |
| 340 // headers. To prepend a cookie (C), three steps are needed: | 332 // headers. To prepend a cookie (C), three steps are needed: |
| 341 // 1) Delete any headers that set cookies (e.g., A, B). | 333 // 1) Delete any headers that set cookies (e.g., A, B). |
| 342 // 2) Append a header which sets the cookie (C). | 334 // 2) Append a header which sets the cookie (C). |
| 343 // 3) Append the original headers (A, B). | 335 // 3) Append the original headers (A, B). |
| 344 // | 336 // |
| 345 // Due to a further limitation of the webRequest API, it is not | 337 // Due to a further limitation of the webRequest API, it is not |
| 346 // possible to delete a header in step 1) and append an identical | 338 // possible to delete a header in step 1) and append an identical |
| 347 // header in step 3). To work around this, a trailing semicolon is | 339 // header in step 3). To work around this, a trailing semicolon is |
| 348 // added to each header before appending it. Trailing semicolons are | 340 // added to each header before appending it. Trailing semicolons are |
| 349 // ignored by Chrome in cookie headers, causing the modified headers | 341 // ignored by Chrome in cookie headers, causing the modified headers |
| 350 // to actually set the original cookies. | 342 // to actually set the original cookies. |
| 351 var otherHeaders = []; | 343 var otherHeaders = []; |
| 352 var cookies = [{name: 'Set-Cookie', | 344 var cookies = |
| 353 value: 'google-accounts-saml-end=now'}]; | 345 [{name: 'Set-Cookie', value: 'google-accounts-saml-end=now'}]; |
| 354 for (var j = 0; j < headers.length; ++j) { | 346 for (var j = 0; j < headers.length; ++j) { |
| 355 if (headers[j].name.toLowerCase().startsWith('set-cookie')) { | 347 if (headers[j].name.toLowerCase().startsWith('set-cookie')) { |
| 356 var header = headers[j]; | 348 var header = headers[j]; |
| 357 header.value += ';'; | 349 header.value += ';'; |
| 358 cookies.push(header); | 350 cookies.push(header); |
| 359 } else { | 351 } else { |
| 360 otherHeaders.push(headers[j]); | 352 otherHeaders.push(headers[j]); |
| 361 } | 353 } |
| 362 } | 354 } |
| 363 return {responseHeaders: otherHeaders.concat(cookies)}; | 355 return {responseHeaders: otherHeaders.concat(cookies)}; |
| 364 } | 356 } |
| 365 } | 357 } |
| 366 } | 358 } |
| 367 } | 359 } |
| 368 | 360 |
| 369 return {}; | 361 return {}; |
| 370 }, | 362 }, |
| 371 | 363 |
| 372 /** | 364 /** |
| 373 * Handler for webRequest.onBeforeSendHeaders. | 365 * Handler for webRequest.onBeforeSendHeaders. |
| 374 * @return {!Object} Modified request headers. | 366 * @return {!Object} Modified request headers. |
| 375 */ | 367 */ |
| 376 onBeforeSendHeaders: function(details) { | 368 onBeforeSendHeaders: function(details) { |
| 377 if (!this.isDesktopFlow_ && this.gaiaUrl_ && | 369 if (!this.isDesktopFlow_ && this.gaiaUrl_ && |
| 378 details.url.startsWith(this.gaiaUrl_)) { | 370 details.url.startsWith(this.gaiaUrl_)) { |
| 379 details.requestHeaders.push({ | 371 details.requestHeaders.push( |
| 380 name: 'X-Cros-Auth-Ext-Support', | 372 {name: 'X-Cros-Auth-Ext-Support', value: 'SAML'}); |
| 381 value: 'SAML' | |
| 382 }); | |
| 383 } | 373 } |
| 384 return {requestHeaders: details.requestHeaders}; | 374 return {requestHeaders: details.requestHeaders}; |
| 385 }, | 375 }, |
| 386 | 376 |
| 387 /** | 377 /** |
| 388 * Handler for 'setGaiaUrl' signal sent from the main script. | 378 * Handler for 'setGaiaUrl' signal sent from the main script. |
| 389 */ | 379 */ |
| 390 onSetGaiaUrl_: function(msg) { | 380 onSetGaiaUrl_: function(msg) { |
| 391 this.gaiaUrl_ = msg.gaiaUrl; | 381 this.gaiaUrl_ = msg.gaiaUrl; |
| 392 }, | 382 }, |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 442 | 432 |
| 443 onUpdatePassword_: function(msg) { | 433 onUpdatePassword_: function(msg) { |
| 444 if (!this.authStarted_) | 434 if (!this.authStarted_) |
| 445 return; | 435 return; |
| 446 | 436 |
| 447 this.passwordStore_[msg.id] = msg.password; | 437 this.passwordStore_[msg.id] = msg.password; |
| 448 }, | 438 }, |
| 449 | 439 |
| 450 onPageLoaded_: function(msg) { | 440 onPageLoaded_: function(msg) { |
| 451 if (this.channelMain_) | 441 if (this.channelMain_) |
| 452 this.channelMain_.send({name: 'onAuthPageLoaded', | 442 this.channelMain_.send( |
| 453 url: msg.url, | 443 {name: 'onAuthPageLoaded', url: msg.url, isSAMLPage: this.isSAML_}); |
| 454 isSAMLPage: this.isSAML_}); | |
| 455 }, | 444 }, |
| 456 | 445 |
| 457 onGetSAMLFlag_: function(msg) { | 446 onGetSAMLFlag_: function(msg) { |
| 458 return this.isSAML_; | 447 return this.isSAML_; |
| 459 } | 448 } |
| 460 }; | 449 }; |
| 461 | 450 |
| 462 var backgroundBridgeManager = new BackgroundBridgeManager(); | 451 var backgroundBridgeManager = new BackgroundBridgeManager(); |
| 463 backgroundBridgeManager.run(); | 452 backgroundBridgeManager.run(); |
| OLD | NEW |