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 |