Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / signin / inline_login_handler_impl.cc
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 #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/signin/about_signin_internals_factory.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_promo.h"
19 #include "chrome/browser/sync/profile_sync_service.h"
20 #include "chrome/browser/sync/profile_sync_service_factory.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
23 #include "chrome/browser/ui/sync/one_click_signin_histogram.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/signin/core/browser/about_signin_internals.h"
28 #include "components/signin/core/browser/profile_oauth2_token_service.h"
29 #include "components/signin/core/browser/signin_error_controller.h"
30 #include "components/signin/core/browser/signin_oauth_helper.h"
31 #include "content/public/browser/storage_partition.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_ui.h"
34 #include "google_apis/gaia/gaia_auth_fetcher.h"
35 #include "google_apis/gaia/gaia_auth_util.h"
36 #include "google_apis/gaia/gaia_constants.h"
37 #include "google_apis/gaia/gaia_urls.h"
38 #include "net/base/url_util.h"
39
40 namespace {
41
42 class InlineSigninHelper : public SigninOAuthHelper,
43                            public SigninOAuthHelper::Consumer {
44  public:
45   InlineSigninHelper(
46       base::WeakPtr<InlineLoginHandlerImpl> handler,
47       net::URLRequestContextGetter* getter,
48       Profile* profile,
49       const GURL& current_url,
50       const std::string& email,
51       const std::string& password,
52       const std::string& session_index,
53       bool choose_what_to_sync);
54
55  private:
56   // Overriden from SigninOAuthHelper::Consumer.
57   virtual void OnSigninOAuthInformationAvailable(
58       const std::string& email,
59       const std::string& display_email,
60       const std::string& refresh_token) OVERRIDE;
61   virtual void OnSigninOAuthInformationFailure(
62       const GoogleServiceAuthError& error) OVERRIDE;
63
64   base::WeakPtr<InlineLoginHandlerImpl> handler_;
65   Profile* profile_;
66   GURL current_url_;
67   std::string email_;
68   std::string password_;
69   std::string session_index_;
70   bool choose_what_to_sync_;
71
72   DISALLOW_COPY_AND_ASSIGN(InlineSigninHelper);
73 };
74
75 InlineSigninHelper::InlineSigninHelper(
76     base::WeakPtr<InlineLoginHandlerImpl> handler,
77     net::URLRequestContextGetter* getter,
78     Profile* profile,
79     const GURL& current_url,
80     const std::string& email,
81     const std::string& password,
82     const std::string& session_index,
83     bool choose_what_to_sync)
84     : SigninOAuthHelper(getter, session_index, this),
85       handler_(handler),
86       profile_(profile),
87       current_url_(current_url),
88       email_(email),
89       password_(password),
90       session_index_(session_index),
91       choose_what_to_sync_(choose_what_to_sync) {
92   DCHECK(profile_);
93   DCHECK(!email_.empty());
94   DCHECK(!session_index_.empty());
95 }
96
97 void InlineSigninHelper::OnSigninOAuthInformationAvailable(
98     const std::string& email,
99     const std::string& display_email,
100     const std::string& refresh_token) {
101   content::WebContents* contents = NULL;
102   Browser* browser = NULL;
103   if (handler_) {
104     contents = handler_->web_ui()->GetWebContents();
105     browser = handler_->GetDesktopBrowser();
106   }
107
108   AboutSigninInternals* about_signin_internals =
109     AboutSigninInternalsFactory::GetForProfile(profile_);
110   about_signin_internals->OnRefreshTokenReceived("Successful");
111
112   signin::Source source = signin::GetSourceForPromoURL(current_url_);
113   if (source == signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT) {
114     ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
115         UpdateCredentials(email, refresh_token);
116
117     if (signin::IsAutoCloseEnabledInURL(current_url_)) {
118       // Close the gaia sign in tab via a task to make sure we aren't in the
119       // middle of any webui handler code.
120       base::MessageLoop::current()->PostTask(
121           FROM_HERE,
122           base::Bind(&InlineLoginHandlerImpl::CloseTab,
123           handler_));
124     }
125   } else {
126     ProfileSyncService* sync_service =
127         ProfileSyncServiceFactory::GetForProfile(profile_);
128     SigninErrorController* error_controller =
129         ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
130             signin_error_controller();
131     OneClickSigninSyncStarter::StartSyncMode start_mode =
132         source == signin::SOURCE_SETTINGS || choose_what_to_sync_ ?
133             (error_controller->HasError() &&
134               sync_service && sync_service->HasSyncSetupCompleted()) ?
135                 OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE :
136                 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST :
137                 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS;
138     OneClickSigninSyncStarter::ConfirmationRequired confirmation_required =
139         source == signin::SOURCE_SETTINGS ||
140         source == signin::SOURCE_WEBSTORE_INSTALL ||
141         choose_what_to_sync_ ?
142             OneClickSigninSyncStarter::NO_CONFIRMATION :
143             OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
144     bool start_signin = true;
145
146     if (source != signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT) {
147       start_signin = !OneClickSigninHelper::HandleCrossAccountError(
148             contents, "",
149             email, password_, refresh_token,
150             OneClickSigninHelper::AUTO_ACCEPT_EXPLICIT,
151             source, start_mode,
152             base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback,
153                        handler_));
154     }
155
156     if (start_signin) {
157       // Call OneClickSigninSyncStarter to exchange oauth code for tokens.
158       // OneClickSigninSyncStarter will delete itself once the job is done.
159       new OneClickSigninSyncStarter(
160           profile_, browser,
161           email, password_, refresh_token,
162           start_mode,
163           contents,
164           confirmation_required,
165           signin::GetNextPageURLForPromoURL(current_url_),
166           base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
167     }
168   }
169
170   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
171 }
172
173 void InlineSigninHelper::OnSigninOAuthInformationFailure(
174   const GoogleServiceAuthError& error) {
175   if (handler_)
176     handler_->HandleLoginError(error.ToString());
177
178   AboutSigninInternals* about_signin_internals =
179     AboutSigninInternalsFactory::GetForProfile(profile_);
180   about_signin_internals->OnRefreshTokenReceived("Failure");
181
182   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
183 }
184
185 }  // namespace
186
187 InlineLoginHandlerImpl::InlineLoginHandlerImpl()
188       : weak_factory_(this),
189         choose_what_to_sync_(false) {
190 }
191
192 InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
193
194 void InlineLoginHandlerImpl::RegisterMessages() {
195   InlineLoginHandler::RegisterMessages();
196
197   web_ui()->RegisterMessageCallback("switchToFullTab",
198       base::Bind(&InlineLoginHandlerImpl::HandleSwitchToFullTabMessage,
199                   base::Unretained(this)));
200 }
201
202 void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) {
203   params.SetInteger("authMode", InlineLoginHandler::kDesktopAuthMode);
204
205   const GURL& current_url = web_ui()->GetWebContents()->GetURL();
206   signin::Source source = signin::GetSourceForPromoURL(current_url);
207   if (source == signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT ||
208       source == signin::SOURCE_AVATAR_BUBBLE_SIGN_IN) {
209     // Drop the leading slash in the path.
210     params.SetString("gaiaPath",
211         GaiaUrls::GetInstance()->embedded_signin_url().path().substr(1));
212   }
213
214   params.SetString("service", "chromiumsync");
215   params.SetString("continueUrl",
216       signin::GetLandingURL("source", static_cast<int>(source)).spec());
217
218   std::string default_email;
219   if (source != signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT) {
220     default_email = Profile::FromWebUI(web_ui())->GetPrefs()->
221         GetString(prefs::kGoogleServicesLastUsername);
222   } else {
223     if (!net::GetValueForKeyInQuery(current_url, "email", &default_email))
224       default_email.clear();
225   }
226   if (!default_email.empty())
227     params.SetString("email", default_email);
228
229   std::string frame_url;
230   net::GetValueForKeyInQuery(current_url, "frameUrl", &frame_url);
231   if (!frame_url.empty())
232     params.SetString("frameUrl", frame_url);
233
234   std::string is_constrained;
235   net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained);
236   if (!is_constrained.empty())
237     params.SetString("constrained", is_constrained);
238
239   // TODO(rogerta): this needs to be passed on to gaia somehow.
240   std::string read_only_email;
241   net::GetValueForKeyInQuery(current_url, "readOnlyEmail", &read_only_email);
242   if (!read_only_email.empty())
243     params.SetString("readOnlyEmail", read_only_email);
244
245   OneClickSigninHelper::LogHistogramValue(
246       source, one_click_signin::HISTOGRAM_SHOWN);
247 }
248
249 void InlineLoginHandlerImpl::HandleSwitchToFullTabMessage(
250     const base::ListValue* args) {
251   base::string16 url_str;
252   CHECK(args->GetString(0, &url_str));
253
254   content::WebContents* web_contents = web_ui()->GetWebContents();
255   GURL main_frame_url(web_contents->GetURL());
256   main_frame_url = net::AppendOrReplaceQueryParameter(
257       main_frame_url, "frameUrl", base::UTF16ToASCII(url_str));
258   chrome::NavigateParams params(
259       Profile::FromWebUI(web_ui()),
260       net::AppendOrReplaceQueryParameter(main_frame_url, "constrained", "0"),
261       content::PAGE_TRANSITION_AUTO_TOPLEVEL);
262   chrome::Navigate(&params);
263
264   web_ui()->CallJavascriptFunction("inline.login.closeDialog");
265 }
266
267 void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) {
268   content::WebContents* contents = web_ui()->GetWebContents();
269   const GURL& current_url = contents->GetURL();
270
271   const base::DictionaryValue* dict = NULL;
272   args->GetDictionary(0, &dict);
273
274   bool skip_for_now = false;
275   dict->GetBoolean("skipForNow", &skip_for_now);
276   if (skip_for_now) {
277     signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
278     SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
279     return;
280   }
281
282   base::string16 email;
283   dict->GetString("email", &email);
284   DCHECK(!email.empty());
285   email_ = base::UTF16ToASCII(email);
286   base::string16 password;
287   dict->GetString("password", &password);
288   password_ = base::UTF16ToASCII(password);
289
290   // When doing a SAML sign in, this email check may result in a false
291   // positive.  This happens when the user types one email address in the
292   // gaia sign in page, but signs in to a different account in the SAML sign in
293   // page.
294   std::string default_email;
295   std::string validate_email;
296   if (net::GetValueForKeyInQuery(current_url, "email", &default_email) &&
297       net::GetValueForKeyInQuery(current_url, "validateEmail",
298                                  &validate_email) &&
299       validate_email == "1") {
300     if (!gaia::AreEmailsSame(email_, default_email)) {
301       SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
302       return;
303     }
304   }
305
306   base::string16 session_index;
307   dict->GetString("sessionIndex", &session_index);
308   session_index_ = base::UTF16ToASCII(session_index);
309   DCHECK(!session_index_.empty());
310   dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync_);
311
312   signin::Source source = signin::GetSourceForPromoURL(current_url);
313   OneClickSigninHelper::LogHistogramValue(
314       source, one_click_signin::HISTOGRAM_ACCEPTED);
315   bool switch_to_advanced =
316       choose_what_to_sync_ && (source != signin::SOURCE_SETTINGS);
317   OneClickSigninHelper::LogHistogramValue(
318       source,
319       switch_to_advanced ? one_click_signin::HISTOGRAM_WITH_ADVANCED :
320                            one_click_signin::HISTOGRAM_WITH_DEFAULTS);
321
322   OneClickSigninHelper::CanOfferFor can_offer_for =
323       source == signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT ?
324       OneClickSigninHelper::CAN_OFFER_FOR_SECONDARY_ACCOUNT :
325       OneClickSigninHelper::CAN_OFFER_FOR_ALL;
326   std::string error_msg;
327   bool can_offer = OneClickSigninHelper::CanOffer(
328       contents, can_offer_for, email_, &error_msg);
329   if (!can_offer) {
330     HandleLoginError(error_msg);
331     return;
332   }
333
334   AboutSigninInternals* about_signin_internals =
335       AboutSigninInternalsFactory::GetForProfile(Profile::FromWebUI(web_ui()));
336   about_signin_internals->OnAuthenticationResultReceived(
337       "GAIA Auth Successful");
338
339   content::StoragePartition* partition =
340       content::BrowserContext::GetStoragePartitionForSite(
341           contents->GetBrowserContext(),
342           GURL(chrome::kChromeUIChromeSigninURL));
343
344   // InlineSigninHelper will delete itself.
345   new InlineSigninHelper(GetWeakPtr(), partition->GetURLRequestContext(),
346                          Profile::FromWebUI(web_ui()), current_url,
347                          email_, password_, session_index_,
348                          choose_what_to_sync_);
349
350   email_.clear();
351   password_.clear();
352   session_index_.clear();
353   web_ui()->CallJavascriptFunction("inline.login.closeDialog");
354 }
355
356 void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg) {
357   SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
358
359   Browser* browser = GetDesktopBrowser();
360   if (browser && !error_msg.empty()) {
361     VLOG(1) << "InlineLoginHandlerImpl::HandleLoginError shows error message: "
362             << error_msg;
363     OneClickSigninHelper::ShowSigninErrorBubble(browser, error_msg);
364   }
365
366   email_.clear();
367   password_.clear();
368   session_index_.clear();
369 }
370
371 Browser* InlineLoginHandlerImpl::GetDesktopBrowser() {
372   Browser* browser = chrome::FindBrowserWithWebContents(
373       web_ui()->GetWebContents());
374   if (!browser) {
375     browser = chrome::FindLastActiveWithProfile(
376         Profile::FromWebUI(web_ui()), chrome::GetActiveDesktop());
377   }
378   return browser;
379 }
380
381 void InlineLoginHandlerImpl::SyncStarterCallback(
382     OneClickSigninSyncStarter::SyncSetupResult result) {
383   content::WebContents* contents = web_ui()->GetWebContents();
384
385   if (contents->GetController().GetPendingEntry()) {
386     // Do nothing if a navigation is pending, since this call can be triggered
387     // from DidStartLoading. This avoids deleting the pending entry while we are
388     // still navigating to it. See crbug/346632.
389     return;
390   }
391
392   const GURL& current_url = contents->GetLastCommittedURL();
393   signin::Source source = signin::GetSourceForPromoURL(current_url);
394   bool auto_close = signin::IsAutoCloseEnabledInURL(current_url);
395
396   if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) {
397     OneClickSigninHelper::RedirectToNtpOrAppsPage(contents, source);
398   } else if (auto_close) {
399     base::MessageLoop::current()->PostTask(
400         FROM_HERE,
401         base::Bind(&InlineLoginHandlerImpl::CloseTab,
402                    weak_factory_.GetWeakPtr()));
403   } else {
404      OneClickSigninHelper::RedirectToNtpOrAppsPageIfNecessary(contents, source);
405   }
406 }
407
408 void InlineLoginHandlerImpl::CloseTab() {
409   content::WebContents* tab = web_ui()->GetWebContents();
410   Browser* browser = chrome::FindBrowserWithWebContents(tab);
411   if (browser) {
412     TabStripModel* tab_strip_model = browser->tab_strip_model();
413     if (tab_strip_model) {
414       int index = tab_strip_model->GetIndexOfWebContents(tab);
415       if (index != TabStripModel::kNoTab) {
416         tab_strip_model->ExecuteContextMenuCommand(
417             index, TabStripModel::CommandCloseTab);
418       }
419     }
420   }
421 }