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 |