a571c10e8561c6e0d9fb885300f8adfb7e7cdb45
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / gaia_auth / background.js
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
3 // found in the LICENSE file.
4
5 /**
6  * @fileoverview
7  * A background script of the auth extension that bridges the communication
8  * between the main and injected scripts.
9  *
10  * Here is an overview of the communication flow when SAML is being used:
11  * 1. The main script sends the |startAuth| signal to this background script,
12  *    indicating that the authentication flow has started and SAML pages may be
13  *    loaded from now on.
14  * 2. A script is injected into each SAML page. The injected script sends three
15  *    main types of messages to this background script:
16  *    a) A |pageLoaded| message is sent when the page has been loaded. This is
17  *       forwarded to the main script as |onAuthPageLoaded|.
18  *    b) If the SAML provider supports the credential passing API, the API calls
19  *       are sent to this backgroudn script as |apiCall| messages. These
20  *       messages are forwarded unmodified to the main script.
21  *    c) The injected script scrapes passwords. They are sent to this background
22  *       script in |updatePassword| messages. The main script can request a list
23  *       of the scraped passwords by sending the |getScrapedPasswords| message.
24  */
25
26 /**
27  * BackgroundBridge allows the main script and the injected script to
28  * collaborate. It forwards credentials API calls to the main script and
29  * maintains a list of scraped passwords.
30  */
31 function BackgroundBridge() {
32 }
33
34 BackgroundBridge.prototype = {
35   // Gaia URL base that is set from main auth script.
36   gaiaUrl_: null,
37
38   // Whether auth flow has started. It is used as a signal of whether the
39   // injected script should scrape passwords.
40   authStarted_: false,
41
42   passwordStore_: {},
43
44   channelMain_: null,
45   channelInjected_: null,
46
47   run: function() {
48     chrome.runtime.onConnect.addListener(this.onConnect_.bind(this));
49
50     // Workarounds for loading SAML page in an iframe.
51     chrome.webRequest.onHeadersReceived.addListener(
52         function(details) {
53           if (!this.authStarted_)
54             return;
55
56           var headers = details.responseHeaders;
57           for (var i = 0; headers && i < headers.length; ++i) {
58             if (headers[i].name.toLowerCase() == 'x-frame-options') {
59               headers.splice(i, 1);
60               break;
61             }
62           }
63           return {responseHeaders: headers};
64         }.bind(this),
65         {urls: ['<all_urls>'], types: ['sub_frame']},
66         ['blocking', 'responseHeaders']);
67   },
68
69   onConnect_: function(port) {
70     if (port.name == 'authMain')
71       this.setupForAuthMain_(port);
72     else if (port.name == 'injected')
73       this.setupForInjected_(port);
74     else
75       console.error('Unexpected connection, port.name=' + port.name);
76   },
77
78   /**
79    * Sets up the communication channel with the main script.
80    */
81   setupForAuthMain_: function(port) {
82     this.channelMain_ = new Channel();
83     this.channelMain_.init(port);
84     this.channelMain_.registerMessage(
85         'setGaiaUrl', this.onSetGaiaUrl_.bind(this));
86     this.channelMain_.registerMessage(
87         'resetAuth', this.onResetAuth_.bind(this));
88     this.channelMain_.registerMessage(
89         'startAuth', this.onAuthStarted_.bind(this));
90     this.channelMain_.registerMessage(
91         'getScrapedPasswords',
92         this.onGetScrapedPasswords_.bind(this));
93   },
94
95   /**
96    * Sets up the communication channel with the injected script.
97    */
98   setupForInjected_: function(port) {
99     this.channelInjected_ = new Channel();
100     this.channelInjected_.init(port);
101     this.channelInjected_.registerMessage(
102         'apiCall', this.onAPICall_.bind(this));
103     this.channelInjected_.registerMessage(
104         'updatePassword', this.onUpdatePassword_.bind(this));
105     this.channelInjected_.registerMessage(
106         'pageLoaded', this.onPageLoaded_.bind(this));
107   },
108
109   /**
110    * Handler for 'setGaiaUrl' signal sent from the main script.
111    */
112   onSetGaiaUrl_: function(msg) {
113     this.gaiaUrl_ = msg.gaiaUrl;
114
115     // Set request header to let Gaia know that saml support is on.
116     chrome.webRequest.onBeforeSendHeaders.addListener(
117         function(details) {
118           details.requestHeaders.push({
119             name: 'X-Cros-Auth-Ext-Support',
120             value: 'SAML'
121           });
122           return {requestHeaders: details.requestHeaders};
123         },
124         {urls: [this.gaiaUrl_ + '*'], types: ['sub_frame']},
125         ['blocking', 'requestHeaders']);
126   },
127
128   /**
129    * Handler for 'resetAuth' signal sent from the main script.
130    */
131   onResetAuth_: function() {
132     this.authStarted_ = false;
133     this.passwordStore_ = {};
134   },
135
136   /**
137    * Handler for 'authStarted' signal sent from the main script.
138    */
139   onAuthStarted_: function() {
140     this.authStarted_ = true;
141     this.passwordStore_ = {};
142   },
143
144   /**
145    * Handler for 'getScrapedPasswords' request sent from the main script.
146    * @return {Array.<string>} The array with de-duped scraped passwords.
147    */
148   onGetScrapedPasswords_: function() {
149     var passwords = {};
150     for (var property in this.passwordStore_) {
151       passwords[this.passwordStore_[property]] = true;
152     }
153     return Object.keys(passwords);
154   },
155
156   onAPICall_: function(msg) {
157     this.channelMain_.send(msg);
158   },
159
160   onUpdatePassword_: function(msg) {
161     if (!this.authStarted_)
162       return;
163
164     this.passwordStore_[msg.id] = msg.password;
165   },
166
167   onPageLoaded_: function(msg) {
168     this.channelMain_.send({name: 'onAuthPageLoaded', url: msg.url});
169   }
170 };
171
172 var backgroundBridge = new BackgroundBridge();
173 backgroundBridge.run();