| 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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 return this.bridges_[details.tabId].onBeforeSendHeaders(details); | 51 return this.bridges_[details.tabId].onBeforeSendHeaders(details); |
| 52 else | 52 else |
| 53 return {requestHeaders: details.requestHeaders}; | 53 return {requestHeaders: details.requestHeaders}; |
| 54 }.bind(this), | 54 }.bind(this), |
| 55 {urls: ['*://*/*'], types: ['sub_frame']}, | 55 {urls: ['*://*/*'], types: ['sub_frame']}, |
| 56 ['blocking', 'requestHeaders']); | 56 ['blocking', 'requestHeaders']); |
| 57 | 57 |
| 58 chrome.webRequest.onHeadersReceived.addListener( | 58 chrome.webRequest.onHeadersReceived.addListener( |
| 59 function(details) { | 59 function(details) { |
| 60 if (this.bridges_[details.tabId]) | 60 if (this.bridges_[details.tabId]) |
| 61 this.bridges_[details.tabId].onHeadersReceived(details); | 61 return this.bridges_[details.tabId].onHeadersReceived(details); |
| 62 }.bind(this), | 62 }.bind(this), |
| 63 {urls: ['*://*/*'], types: ['sub_frame']}, | 63 {urls: ['*://*/*'], types: ['sub_frame']}, |
| 64 ['responseHeaders']); | 64 ['blocking', 'responseHeaders']); |
| 65 | 65 |
| 66 chrome.webRequest.onCompleted.addListener( | 66 chrome.webRequest.onCompleted.addListener( |
| 67 function(details) { | 67 function(details) { |
| 68 if (this.bridges_[details.tabId]) | 68 if (this.bridges_[details.tabId]) |
| 69 this.bridges_[details.tabId].onCompleted(details); | 69 this.bridges_[details.tabId].onCompleted(details); |
| 70 }.bind(this), | 70 }.bind(this), |
| 71 {urls: ['*://*/*'], types: ['sub_frame']}, | 71 {urls: ['*://*/*'], types: ['sub_frame']}, |
| 72 ['responseHeaders']); | 72 ['responseHeaders']); |
| 73 }, | 73 }, |
| 74 | 74 |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 onInsecureRequest: function(url) { | 253 onInsecureRequest: function(url) { |
| 254 if (!this.blockInsecureContent_) | 254 if (!this.blockInsecureContent_) |
| 255 return {}; | 255 return {}; |
| 256 this.channelMain_.send({name: 'onInsecureContentBlocked', url: url}); | 256 this.channelMain_.send({name: 'onInsecureContentBlocked', url: url}); |
| 257 return {cancel: true}; | 257 return {cancel: true}; |
| 258 }, | 258 }, |
| 259 | 259 |
| 260 /** | 260 /** |
| 261 * Handler or webRequest.onHeadersReceived. It reads the authenticated user | 261 * Handler or webRequest.onHeadersReceived. It reads the authenticated user |
| 262 * email from google-accounts-signin-header. | 262 * email from google-accounts-signin-header. |
| 263 * @return {!Object} Modified request headers. |
| 263 */ | 264 */ |
| 264 onHeadersReceived: function(details) { | 265 onHeadersReceived: function(details) { |
| 265 if (!this.isDesktopFlow_ || | 266 var headers = details.responseHeaders; |
| 266 !this.gaiaUrl_ || | 267 |
| 267 details.url.lastIndexOf(this.gaiaUrl_) != 0) { | 268 if (this.isDesktopFlow_ && |
| 269 this.gaiaUrl_ && |
| 270 details.url.lastIndexOf(this.gaiaUrl_) == 0) { |
| 268 // TODO(xiyuan, guohui): CrOS should reuse the logic below for reading the | 271 // TODO(xiyuan, guohui): CrOS should reuse the logic below for reading the |
| 269 // email for SAML users and cut off the /ListAccount call. | 272 // email for SAML users and cut off the /ListAccount call. |
| 270 return; | 273 for (var i = 0; headers && i < headers.length; ++i) { |
| 274 if (headers[i].name.toLowerCase() == 'google-accounts-signin') { |
| 275 var headerValues = headers[i].value.toLowerCase().split(','); |
| 276 var signinDetails = {}; |
| 277 headerValues.forEach(function(e) { |
| 278 var pair = e.split('='); |
| 279 signinDetails[pair[0].trim()] = pair[1].trim(); |
| 280 }); |
| 281 // Remove "" around. |
| 282 this.email_ = signinDetails['email'].slice(1, -1); |
| 283 this.sessionIndex_ = signinDetails['sessionindex']; |
| 284 break; |
| 285 } |
| 286 } |
| 271 } | 287 } |
| 272 | 288 |
| 273 var headers = details.responseHeaders; | 289 if (!this.isDesktopFlow_) { |
| 274 for (var i = 0; headers && i < headers.length; ++i) { | 290 // Check whether GAIA headers indicating the start or end of a SAML |
| 275 if (headers[i].name.toLowerCase() == 'google-accounts-signin') { | 291 // redirect are present. If so, synthesize cookies to mark these points. |
| 276 var headerValues = headers[i].value.toLowerCase().split(','); | 292 for (var i = 0; headers && i < headers.length; ++i) { |
| 277 var signinDetails = {}; | 293 if (headers[i].name.toLowerCase() == 'google-accounts-saml') { |
| 278 headerValues.forEach(function(e) { | 294 var action = headers[i].value.toLowerCase(); |
| 279 var pair = e.split('='); | 295 if (action == 'start') { |
| 280 signinDetails[pair[0].trim()] = pair[1].trim(); | 296 // GAIA is redirecting to a SAML IdP. Any cookies contained in the |
| 281 }); | 297 // current |headers| were set by GAIA. Any cookies set in future |
| 282 // Remove "" around. | 298 // requests will be coming from the IdP. Append a cookie to the |
| 283 this.email_ = signinDetails['email'].slice(1, -1); | 299 // current |headers| that marks the point at which the redirect |
| 284 this.sessionIndex_ = signinDetails['sessionindex']; | 300 // occurred. |
| 285 return; | 301 headers.push({name: 'Set-Cookie', |
| 302 value: 'google-accounts-saml-start=now'}); |
| 303 return {responseHeaders: headers}; |
| 304 } else if (action == 'end') { |
| 305 // The SAML IdP has redirected back to GAIA. Add a cookie that marks |
| 306 // the point at which the redirect occurred occurred. It is |
| 307 // important that this cookie be prepended to the current |headers| |
| 308 // because any cookies contained in the |headers| were already set |
| 309 // by GAIA, not the IdP. Due to limitations in the webRequest API, |
| 310 // it is not trivial to prepend a cookie: |
| 311 // |
| 312 // The webRequest API only allows for deleting and appending |
| 313 // headers. To prepend a cookie (C), three steps are needed: |
| 314 // 1) Delete any headers that set cookies (e.g., A, B). |
| 315 // 2) Append a header which sets the cookie (C). |
| 316 // 3) Append the original headers (A, B). |
| 317 // |
| 318 // Due to a further limitation of the webRequest API, it is not |
| 319 // possible to delete a header in step 1) and append an identical |
| 320 // header in step 3). To work around this, a trailing semicolon is |
| 321 // added to each header before appending it. Trailing semicolons are |
| 322 // ignored by Chrome in cookie headers, causing the modified headers |
| 323 // to actually set the original cookies. |
| 324 var otherHeaders = []; |
| 325 var cookies = [{name: 'Set-Cookie', |
| 326 value: 'google-accounts-saml-end=now'}]; |
| 327 for (var j = 0; j < headers.length; ++j) { |
| 328 if (headers[j].name.toLowerCase().indexOf('set-cookie') == 0) { |
| 329 var header = headers[j]; |
| 330 header.value += ';'; |
| 331 cookies.push(header); |
| 332 } else { |
| 333 otherHeaders.push(headers[j]); |
| 334 } |
| 335 } |
| 336 return {responseHeaders: otherHeaders.concat(cookies)}; |
| 337 } |
| 338 } |
| 286 } | 339 } |
| 287 } | 340 } |
| 341 |
| 342 return {}; |
| 288 }, | 343 }, |
| 289 | 344 |
| 290 /** | 345 /** |
| 291 * Handler for webRequest.onBeforeSendHeaders. | 346 * Handler for webRequest.onBeforeSendHeaders. |
| 292 * @return {!Object} Modified request headers. | 347 * @return {!Object} Modified request headers. |
| 293 */ | 348 */ |
| 294 onBeforeSendHeaders: function(details) { | 349 onBeforeSendHeaders: function(details) { |
| 295 if (!this.isDesktopFlow_ && this.gaiaUrl_ && | 350 if (!this.isDesktopFlow_ && this.gaiaUrl_ && |
| 296 details.url.indexOf(this.gaiaUrl_) == 0) { | 351 details.url.indexOf(this.gaiaUrl_) == 0) { |
| 297 details.requestHeaders.push({ | 352 details.requestHeaders.push({ |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 }, | 419 }, |
| 365 | 420 |
| 366 onPageLoaded_: function(msg) { | 421 onPageLoaded_: function(msg) { |
| 367 if (this.channelMain_) | 422 if (this.channelMain_) |
| 368 this.channelMain_.send({name: 'onAuthPageLoaded', url: msg.url}); | 423 this.channelMain_.send({name: 'onAuthPageLoaded', url: msg.url}); |
| 369 } | 424 } |
| 370 }; | 425 }; |
| 371 | 426 |
| 372 var backgroundBridgeManager = new BackgroundBridgeManager(); | 427 var backgroundBridgeManager = new BackgroundBridgeManager(); |
| 373 backgroundBridgeManager.run(); | 428 backgroundBridgeManager.run(); |
| OLD | NEW |