Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / profile_auth_data.cc
1 // Copyright (c) 2012 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 #include "chrome/browser/chromeos/login/profile_auth_data.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/time/time.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "net/cookies/canonical_cookie.h"
20 #include "net/cookies/cookie_monster.h"
21 #include "net/cookies/cookie_store.h"
22 #include "net/http/http_auth_cache.h"
23 #include "net/http/http_network_session.h"
24 #include "net/http/http_transaction_factory.h"
25 #include "net/ssl/channel_id_service.h"
26 #include "net/ssl/channel_id_store.h"
27 #include "net/url_request/url_request_context.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "url/gurl.h"
30
31 using content::BrowserThread;
32
33 namespace chromeos {
34
35 namespace {
36
37 const char kSAMLStartCookie[] = "google-accounts-saml-start";
38 const char kSAMLEndCookie[] = "google-accounts-saml-end";
39
40 class ProfileAuthDataTransferer {
41  public:
42   ProfileAuthDataTransferer(
43       content::BrowserContext* from_context,
44       content::BrowserContext* to_context,
45       bool transfer_auth_cookies_and_channel_ids_on_first_login,
46       bool transfer_saml_auth_cookies_on_subsequent_login,
47       const base::Closure& completion_callback);
48
49   void BeginTransfer();
50
51  private:
52   void BeginTransferOnIOThread();
53
54   // Transfer the proxy auth cache from |from_context_| to |to_context_|. If
55   // the user was required to authenticate with a proxy during login, this
56   // authentication information will be transferred into the user's session.
57   void TransferProxyAuthCache();
58
59   // Callback that receives the content of |to_context_|'s cookie jar. Checks
60   // whether this is the user's first login, based on the state of the cookie
61   // jar, and starts retrieval of the data that should be transfered. Calls
62   // Finish() if there is no data to transfer.
63   void OnTargetCookieJarContentsRetrieved(
64       const net::CookieList& target_cookies);
65
66   // Retrieve the contents of |from_context_|'s cookie jar. When the retrieval
67   // finishes, OnCookiesToTransferRetrieved will be called with the result.
68   void RetrieveCookiesToTransfer();
69
70   // Callback that receives the contents of |from_context_|'s cookie jar. Calls
71   // MaybeTransferCookiesAndChannelIDs() to try and perform the transfer.
72   void OnCookiesToTransferRetrieved(const net::CookieList& cookies_to_transfer);
73
74   // Retrieve |from_context_|'s channel IDs. When the retrieval finishes,
75   // OnChannelIDsToTransferRetrieved will be called with the result.
76   void RetrieveChannelIDsToTransfer();
77
78   // Callback that receives |from_context_|'s channel IDs. Calls
79   // MaybeTransferCookiesAndChannelIDs() to try and perform the transfer.
80   void OnChannelIDsToTransferRetrieved(
81       const net::ChannelIDStore::ChannelIDList& channel_ids_to_transfer);
82
83   // Given a |cookie| set during login, returns true if the cookie may have been
84   // set by GAIA. The main criterion is the |cookie|'s creation date. The points
85   // in time at which redirects from GAIA to SAML IdP and back occur are stored
86   // in |saml_start_time_| and |saml_end_time_|. If the cookie was set between
87   // these two times, it was created by the SAML IdP. Otherwise, it was created
88   // by GAIA.
89   // As an additional precaution, the cookie's domain is checked. If the domain
90   // contains "google" or "youtube", the cookie is considered to have been set
91   // by GAIA as well.
92   bool IsGAIACookie(const net::CanonicalCookie& cookie);
93
94   // If all data to be transferred has been retrieved already, transfer it to
95   // |to_context_| and call Finish().
96   void MaybeTransferCookiesAndChannelIDs();
97
98   // Post the |completion_callback_| to the UI thread and schedule destruction
99   // of |this|.
100   void Finish();
101
102   scoped_refptr<net::URLRequestContextGetter> from_context_;
103   scoped_refptr<net::URLRequestContextGetter> to_context_;
104   bool transfer_auth_cookies_and_channel_ids_on_first_login_;
105   bool transfer_saml_auth_cookies_on_subsequent_login_;
106   base::Closure completion_callback_;
107
108   net::CookieList cookies_to_transfer_;
109   net::ChannelIDStore::ChannelIDList channel_ids_to_transfer_;
110
111   // The time at which a redirect from GAIA to a SAML IdP occurred.
112   base::Time saml_start_time_;
113   // The time at which a redirect from a SAML IdP back to GAIA occurred.
114   base::Time saml_end_time_;
115
116   bool first_login_;
117   bool waiting_for_auth_cookies_;
118   bool waiting_for_channel_ids_;
119 };
120
121 ProfileAuthDataTransferer::ProfileAuthDataTransferer(
122     content::BrowserContext* from_context,
123     content::BrowserContext* to_context,
124     bool transfer_auth_cookies_and_channel_ids_on_first_login,
125     bool transfer_saml_auth_cookies_on_subsequent_login,
126     const base::Closure& completion_callback)
127     : from_context_(from_context->GetRequestContext()),
128       to_context_(to_context->GetRequestContext()),
129       transfer_auth_cookies_and_channel_ids_on_first_login_(
130           transfer_auth_cookies_and_channel_ids_on_first_login),
131       transfer_saml_auth_cookies_on_subsequent_login_(
132           transfer_saml_auth_cookies_on_subsequent_login),
133       completion_callback_(completion_callback),
134       first_login_(false),
135       waiting_for_auth_cookies_(false),
136       waiting_for_channel_ids_(false) {
137 }
138
139 void ProfileAuthDataTransferer::BeginTransfer() {
140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
141   // If we aren't transferring auth cookies or channel IDs, post the completion
142   // callback immediately. Otherwise, it will be called when the transfer
143   // finishes.
144   if (!transfer_auth_cookies_and_channel_ids_on_first_login_ &&
145       !transfer_saml_auth_cookies_on_subsequent_login_) {
146     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_);
147     // Null the callback so that when Finish is called, the callback won't be
148     // called again.
149     completion_callback_.Reset();
150   }
151   BrowserThread::PostTask(
152       BrowserThread::IO, FROM_HERE,
153       base::Bind(&ProfileAuthDataTransferer::BeginTransferOnIOThread,
154                  base::Unretained(this)));
155 }
156
157 void ProfileAuthDataTransferer::BeginTransferOnIOThread() {
158   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
159   TransferProxyAuthCache();
160   if (transfer_auth_cookies_and_channel_ids_on_first_login_ ||
161       transfer_saml_auth_cookies_on_subsequent_login_) {
162     // Retrieve the contents of |to_context_|'s cookie jar.
163     net::CookieStore* to_store =
164         to_context_->GetURLRequestContext()->cookie_store();
165     net::CookieMonster* to_monster = to_store->GetCookieMonster();
166     to_monster->GetAllCookiesAsync(
167         base::Bind(
168             &ProfileAuthDataTransferer::OnTargetCookieJarContentsRetrieved,
169         base::Unretained(this)));
170   } else {
171     Finish();
172   }
173 }
174
175 void ProfileAuthDataTransferer::TransferProxyAuthCache() {
176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
177   net::HttpAuthCache* new_cache = to_context_->GetURLRequestContext()->
178       http_transaction_factory()->GetSession()->http_auth_cache();
179   new_cache->UpdateAllFrom(*from_context_->GetURLRequestContext()->
180       http_transaction_factory()->GetSession()->http_auth_cache());
181 }
182
183 void ProfileAuthDataTransferer::OnTargetCookieJarContentsRetrieved(
184     const net::CookieList& target_cookies) {
185   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
186   first_login_ = target_cookies.empty();
187   if (first_login_) {
188     // On first login, transfer all auth cookies and channel IDs if
189     // |transfer_auth_cookies_and_channel_ids_on_first_login_| is true.
190     waiting_for_auth_cookies_ =
191         transfer_auth_cookies_and_channel_ids_on_first_login_;
192     waiting_for_channel_ids_ =
193         transfer_auth_cookies_and_channel_ids_on_first_login_;
194   } else {
195     // On subsequent login, transfer auth cookies set by the SAML IdP if
196     // |transfer_saml_auth_cookies_on_subsequent_login_| is true.
197     waiting_for_auth_cookies_ = transfer_saml_auth_cookies_on_subsequent_login_;
198   }
199
200   if (!waiting_for_auth_cookies_ && !waiting_for_channel_ids_) {
201     Finish();
202     return;
203   }
204
205   if (waiting_for_auth_cookies_)
206     RetrieveCookiesToTransfer();
207   if (waiting_for_channel_ids_)
208     RetrieveChannelIDsToTransfer();
209 }
210
211 void ProfileAuthDataTransferer::RetrieveCookiesToTransfer() {
212   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
213   net::CookieStore* from_store =
214       from_context_->GetURLRequestContext()->cookie_store();
215   net::CookieMonster* from_monster = from_store->GetCookieMonster();
216   from_monster->SetKeepExpiredCookies();
217   from_monster->GetAllCookiesAsync(
218       base::Bind(&ProfileAuthDataTransferer::OnCookiesToTransferRetrieved,
219                  base::Unretained(this)));
220 }
221
222 void ProfileAuthDataTransferer::OnCookiesToTransferRetrieved(
223     const net::CookieList& cookies_to_transfer) {
224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225   waiting_for_auth_cookies_ = false;
226   cookies_to_transfer_ = cookies_to_transfer;
227
228   // Look for cookies indicating the points in time at which redirects from GAIA
229   // to SAML IdP and back occurred. These cookies are synthesized by
230   // chrome/browser/resources/gaia_auth/background.js. If the cookies are found,
231   // their creation times are stored in |saml_start_time_| and
232   // |cookies_to_transfer_| and the cookies are deleted.
233   for (net::CookieList::iterator it = cookies_to_transfer_.begin();
234        it != cookies_to_transfer_.end(); ) {
235     if (it->Name() == kSAMLStartCookie) {
236       saml_start_time_ = it->CreationDate();
237       it = cookies_to_transfer_.erase(it);
238     } else if (it->Name() == kSAMLEndCookie) {
239       saml_end_time_ = it->CreationDate();
240       it = cookies_to_transfer_.erase(it);
241     } else {
242       ++it;
243     }
244   }
245
246   MaybeTransferCookiesAndChannelIDs();
247 }
248
249 void ProfileAuthDataTransferer::RetrieveChannelIDsToTransfer() {
250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
251   net::ChannelIDService* from_service =
252       from_context_->GetURLRequestContext()->channel_id_service();
253   from_service->GetChannelIDStore()->GetAllChannelIDs(
254       base::Bind(
255           &ProfileAuthDataTransferer::OnChannelIDsToTransferRetrieved,
256           base::Unretained(this)));
257 }
258
259 void ProfileAuthDataTransferer::OnChannelIDsToTransferRetrieved(
260     const net::ChannelIDStore::ChannelIDList& channel_ids_to_transfer) {
261   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262   channel_ids_to_transfer_ = channel_ids_to_transfer;
263   waiting_for_channel_ids_ = false;
264   MaybeTransferCookiesAndChannelIDs();
265 }
266
267 bool ProfileAuthDataTransferer::IsGAIACookie(
268     const net::CanonicalCookie& cookie) {
269   const base::Time& creation_date = cookie.CreationDate();
270   if (creation_date < saml_start_time_)
271     return true;
272   if (!saml_end_time_.is_null() && creation_date > saml_end_time_)
273     return true;
274
275   const std::string& domain = cookie.Domain();
276   return domain.find("google") != std::string::npos ||
277          domain.find("youtube") != std::string::npos;
278 }
279
280 void ProfileAuthDataTransferer::MaybeTransferCookiesAndChannelIDs() {
281   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
282   if (waiting_for_auth_cookies_ || waiting_for_channel_ids_)
283     return;
284
285   net::CookieStore* to_store =
286       to_context_->GetURLRequestContext()->cookie_store();
287   net::CookieMonster* to_monster = to_store->GetCookieMonster();
288   if (first_login_) {
289     to_monster->ImportCookies(cookies_to_transfer_);
290     net::ChannelIDService* to_cert_service =
291         to_context_->GetURLRequestContext()->channel_id_service();
292     to_cert_service->GetChannelIDStore()->InitializeFrom(
293         channel_ids_to_transfer_);
294   } else {
295     net::CookieList non_gaia_cookies;
296     for (net::CookieList::const_iterator it = cookies_to_transfer_.begin();
297          it != cookies_to_transfer_.end(); ++it) {
298       if (!IsGAIACookie(*it))
299         non_gaia_cookies.push_back(*it);
300     }
301     to_monster->ImportCookies(non_gaia_cookies);
302   }
303
304   Finish();
305 }
306
307 void ProfileAuthDataTransferer::Finish() {
308   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
309   if (!completion_callback_.is_null())
310     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_);
311   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
312 }
313
314 }  // namespace
315
316 void ProfileAuthData::Transfer(
317     content::BrowserContext* from_context,
318     content::BrowserContext* to_context,
319     bool transfer_auth_cookies_and_channel_ids_on_first_login,
320     bool transfer_saml_auth_cookies_on_subsequent_login,
321     const base::Closure& completion_callback) {
322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323   (new ProfileAuthDataTransferer(
324        from_context,
325        to_context,
326        transfer_auth_cookies_and_channel_ids_on_first_login,
327        transfer_saml_auth_cookies_on_subsequent_login,
328        completion_callback))->BeginTransfer();
329 }
330
331 }  // namespace chromeos