ProfilePolicyConnectorFactory: Refactoring from Profile to BrowserContext.
[chromium-blink-merge.git] / chrome / browser / ui / webui / signin / inline_login_handler_impl.cc
blobf58baf7535e8cf16462aaace5fbc3ca59e2a001c
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.
5 #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_window.h"
16 #include "chrome/browser/signin/about_signin_internals_factory.h"
17 #include "chrome/browser/signin/account_tracker_service_factory.h"
18 #include "chrome/browser/signin/chrome_signin_client_factory.h"
19 #include "chrome/browser/signin/local_auth.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21 #include "chrome/browser/signin/signin_error_controller_factory.h"
22 #include "chrome/browser/signin/signin_manager_factory.h"
23 #include "chrome/browser/signin/signin_promo.h"
24 #include "chrome/browser/sync/profile_sync_service.h"
25 #include "chrome/browser/sync/profile_sync_service_factory.h"
26 #include "chrome/browser/ui/browser_finder.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
30 #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
31 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
32 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
33 #include "chrome/common/url_constants.h"
34 #include "components/signin/core/browser/about_signin_internals.h"
35 #include "components/signin/core/browser/account_tracker_service.h"
36 #include "components/signin/core/browser/profile_oauth2_token_service.h"
37 #include "components/signin/core/browser/signin_error_controller.h"
38 #include "components/signin/core/browser/signin_metrics.h"
39 #include "components/signin/core/common/profile_management_switches.h"
40 #include "content/public/browser/storage_partition.h"
41 #include "content/public/browser/web_ui.h"
42 #include "google_apis/gaia/gaia_auth_consumer.h"
43 #include "google_apis/gaia/gaia_auth_fetcher.h"
44 #include "google_apis/gaia/gaia_auth_util.h"
45 #include "google_apis/gaia/gaia_constants.h"
46 #include "google_apis/gaia/gaia_urls.h"
47 #include "net/base/url_util.h"
49 namespace {
51 class InlineSigninHelper : public GaiaAuthConsumer {
52 public:
53 InlineSigninHelper(
54 base::WeakPtr<InlineLoginHandlerImpl> handler,
55 net::URLRequestContextGetter* getter,
56 Profile* profile,
57 const GURL& current_url,
58 const std::string& email,
59 const std::string& gaia_id,
60 const std::string& password,
61 const std::string& session_index,
62 const std::string& signin_scoped_device_id,
63 bool choose_what_to_sync,
64 bool confirm_untrusted_signin);
66 private:
67 // Overridden from GaiaAuthConsumer.
68 void OnClientOAuthSuccess(const ClientOAuthResult& result) override;
69 void OnClientOAuthFailure(const GoogleServiceAuthError& error)
70 override;
72 GaiaAuthFetcher gaia_auth_fetcher_;
73 base::WeakPtr<InlineLoginHandlerImpl> handler_;
74 Profile* profile_;
75 GURL current_url_;
76 std::string email_;
77 std::string gaia_id_;
78 std::string password_;
79 std::string session_index_;
80 bool choose_what_to_sync_;
81 bool confirm_untrusted_signin_;
83 DISALLOW_COPY_AND_ASSIGN(InlineSigninHelper);
86 InlineSigninHelper::InlineSigninHelper(
87 base::WeakPtr<InlineLoginHandlerImpl> handler,
88 net::URLRequestContextGetter* getter,
89 Profile* profile,
90 const GURL& current_url,
91 const std::string& email,
92 const std::string& gaia_id,
93 const std::string& password,
94 const std::string& session_index,
95 const std::string& signin_scoped_device_id,
96 bool choose_what_to_sync,
97 bool confirm_untrusted_signin)
98 : gaia_auth_fetcher_(this, GaiaConstants::kChromeSource, getter),
99 handler_(handler),
100 profile_(profile),
101 current_url_(current_url),
102 email_(email),
103 gaia_id_(gaia_id),
104 password_(password),
105 session_index_(session_index),
106 choose_what_to_sync_(choose_what_to_sync),
107 confirm_untrusted_signin_(confirm_untrusted_signin) {
108 DCHECK(profile_);
109 DCHECK(!email_.empty());
110 gaia_auth_fetcher_.StartCookieForOAuthLoginTokenExchangeWithDeviceId(
111 session_index, signin_scoped_device_id);
114 void InlineSigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) {
115 content::WebContents* contents = NULL;
116 Browser* browser = NULL;
117 if (handler_) {
118 contents = handler_->web_ui()->GetWebContents();
119 browser = handler_->GetDesktopBrowser();
122 AboutSigninInternals* about_signin_internals =
123 AboutSigninInternalsFactory::GetForProfile(profile_);
124 about_signin_internals->OnRefreshTokenReceived("Successful");
126 AccountTrackerService* account_tracker =
127 AccountTrackerServiceFactory::GetForProfile(profile_);
128 std::string account_id =
129 account_tracker->PickAccountIdForAccount(gaia_id_, email_);
131 // Prime the account tracker with this combination of gaia id/display email.
132 account_tracker->SeedAccountInfo(gaia_id_, email_);
134 signin_metrics::Source source = signin::GetSourceForPromoURL(current_url_);
136 SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
137 std::string primary_email = signin_manager->GetAuthenticatedUsername();
138 if (gaia::AreEmailsSame(email_, primary_email) &&
139 source == signin_metrics::SOURCE_REAUTH &&
140 switches::IsNewProfileManagement() &&
141 !password_.empty() &&
142 profiles::IsLockAvailable(profile_)) {
143 LocalAuth::SetLocalAuthCredentials(profile_, password_);
146 if (source == signin_metrics::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT ||
147 source == signin_metrics::SOURCE_REAUTH) {
148 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
149 UpdateCredentials(account_id, result.refresh_token);
151 if (signin::IsAutoCloseEnabledInURL(current_url_)) {
152 // Close the gaia sign in tab via a task to make sure we aren't in the
153 // middle of any webui handler code.
154 base::MessageLoop::current()->PostTask(
155 FROM_HERE,
156 base::Bind(&InlineLoginHandlerImpl::CloseTab,
157 handler_,
158 signin::ShouldShowAccountManagement(current_url_)));
161 if (source == signin_metrics::SOURCE_REAUTH)
162 signin_manager->MergeSigninCredentialIntoCookieJar();
163 } else {
164 ProfileSyncService* sync_service =
165 ProfileSyncServiceFactory::GetForProfile(profile_);
166 SigninErrorController* error_controller =
167 SigninErrorControllerFactory::GetForProfile(profile_);
169 bool is_new_avatar_menu = switches::IsNewAvatarMenu();
171 OneClickSigninSyncStarter::StartSyncMode start_mode;
172 if (source == signin_metrics::SOURCE_SETTINGS || choose_what_to_sync_) {
173 bool show_settings_without_configure =
174 error_controller->HasError() &&
175 sync_service &&
176 sync_service->HasSyncSetupCompleted();
177 start_mode = show_settings_without_configure ?
178 OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE :
179 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST;
180 } else {
181 start_mode = is_new_avatar_menu ?
182 OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST :
183 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS;
186 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required;
187 if (confirm_untrusted_signin_) {
188 confirmation_required =
189 OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN;
190 } else if (is_new_avatar_menu) {
191 confirmation_required = OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
192 } else {
193 confirmation_required =
194 source == signin_metrics::SOURCE_SETTINGS ||
195 choose_what_to_sync_ ?
196 OneClickSigninSyncStarter::NO_CONFIRMATION :
197 OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
200 bool start_signin =
201 !OneClickSigninHelper::HandleCrossAccountError(
202 profile_, "",
203 email_, password_, result.refresh_token,
204 OneClickSigninHelper::AUTO_ACCEPT_EXPLICIT,
205 source, start_mode,
206 base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback,
207 handler_));
208 if (start_signin) {
209 // Call OneClickSigninSyncStarter to exchange oauth code for tokens.
210 // OneClickSigninSyncStarter will delete itself once the job is done.
211 new OneClickSigninSyncStarter(
212 profile_, browser,
213 email_, password_, result.refresh_token,
214 start_mode,
215 contents,
216 confirmation_required,
217 signin::GetNextPageURLForPromoURL(current_url_),
218 base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
222 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
225 void InlineSigninHelper::OnClientOAuthFailure(
226 const GoogleServiceAuthError& error) {
227 if (handler_)
228 handler_->HandleLoginError(error.ToString());
230 AboutSigninInternals* about_signin_internals =
231 AboutSigninInternalsFactory::GetForProfile(profile_);
232 about_signin_internals->OnRefreshTokenReceived("Failure");
234 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
237 } // namespace
239 InlineLoginHandlerImpl::InlineLoginHandlerImpl()
240 : confirm_untrusted_signin_(false),
241 weak_factory_(this) {
244 InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
246 // This method is not called with webview sign in enabled.
247 void InlineLoginHandlerImpl::DidCommitProvisionalLoadForFrame(
248 content::RenderFrameHost* render_frame_host,
249 const GURL& url,
250 ui::PageTransition transition_type) {
251 if (!web_contents())
252 return;
254 // Returns early if this is not a gaia iframe navigation.
255 const GURL kGaiaExtOrigin(
256 "chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/");
257 content::RenderFrameHost* gaia_iframe = InlineLoginUI::GetAuthIframe(
258 web_contents(), kGaiaExtOrigin, "signin-frame");
259 if (render_frame_host != gaia_iframe)
260 return;
262 // Loading any untrusted (e.g., HTTP) URLs in the privileged sign-in process
263 // will require confirmation before the sign in takes effect.
264 if (!url.is_empty()) {
265 GURL origin(url.GetOrigin());
266 if (url.spec() != url::kAboutBlankURL &&
267 origin != kGaiaExtOrigin &&
268 !gaia::IsGaiaSignonRealm(origin)) {
269 confirm_untrusted_signin_ = true;
274 void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) {
275 params.SetString("service", "chromiumsync");
277 content::WebContents* contents = web_ui()->GetWebContents();
278 const GURL& current_url = contents->GetURL();
279 std::string is_constrained;
280 net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained);
282 content::WebContentsObserver::Observe(contents);
283 OneClickSigninHelper::LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN);
286 void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) {
287 content::WebContents* contents = web_ui()->GetWebContents();
288 const GURL& current_url = contents->GetURL();
290 const base::DictionaryValue* dict = NULL;
291 args->GetDictionary(0, &dict);
293 bool skip_for_now = false;
294 dict->GetBoolean("skipForNow", &skip_for_now);
295 if (skip_for_now) {
296 signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
297 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
298 return;
301 // This value exists only for webview sign in.
302 bool trusted = false;
303 if (dict->GetBoolean("trusted", &trusted))
304 confirm_untrusted_signin_ = !trusted;
306 base::string16 email_string16;
307 dict->GetString("email", &email_string16);
308 DCHECK(!email_string16.empty());
309 std::string email(base::UTF16ToASCII(email_string16));
311 base::string16 password_string16;
312 dict->GetString("password", &password_string16);
313 std::string password(base::UTF16ToASCII(password_string16));
315 base::string16 gaia_id_string16;
316 dict->GetString("gaiaId", &gaia_id_string16);
317 DCHECK(!gaia_id_string16.empty());
318 std::string gaia_id = base::UTF16ToASCII(gaia_id_string16);
320 // When doing a SAML sign in, this email check may result in a false
321 // positive. This happens when the user types one email address in the
322 // gaia sign in page, but signs in to a different account in the SAML sign in
323 // page.
324 std::string default_email;
325 std::string validate_email;
326 if (net::GetValueForKeyInQuery(current_url, "email", &default_email) &&
327 net::GetValueForKeyInQuery(current_url, "validateEmail",
328 &validate_email) &&
329 validate_email == "1") {
330 if (!gaia::AreEmailsSame(email, default_email)) {
331 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
332 return;
336 base::string16 session_index_string16;
337 dict->GetString("sessionIndex", &session_index_string16);
338 std::string session_index = base::UTF16ToASCII(session_index_string16);
339 DCHECK(!session_index.empty());
341 bool choose_what_to_sync = false;
342 dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync);
344 signin_metrics::Source source = signin::GetSourceForPromoURL(current_url);
345 OneClickSigninHelper::LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED);
346 bool switch_to_advanced =
347 choose_what_to_sync && (source != signin_metrics::SOURCE_SETTINGS);
348 OneClickSigninHelper::LogHistogramValue(
349 switch_to_advanced ? signin_metrics::HISTOGRAM_WITH_ADVANCED :
350 signin_metrics::HISTOGRAM_WITH_DEFAULTS);
352 OneClickSigninHelper::CanOfferFor can_offer_for =
353 OneClickSigninHelper::CAN_OFFER_FOR_ALL;
354 switch (source) {
355 case signin_metrics::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT:
356 can_offer_for = OneClickSigninHelper::CAN_OFFER_FOR_SECONDARY_ACCOUNT;
357 break;
358 case signin_metrics::SOURCE_REAUTH: {
359 std::string primary_username =
360 SigninManagerFactory::GetForProfile(
361 Profile::FromWebUI(web_ui()))->GetAuthenticatedUsername();
362 if (!gaia::AreEmailsSame(default_email, primary_username))
363 can_offer_for = OneClickSigninHelper::CAN_OFFER_FOR_SECONDARY_ACCOUNT;
364 break;
366 default:
367 // No need to change |can_offer_for|.
368 break;
371 std::string error_msg;
372 bool can_offer = OneClickSigninHelper::CanOffer(
373 contents, can_offer_for, email, &error_msg);
374 if (!can_offer) {
375 HandleLoginError(error_msg);
376 return;
379 AboutSigninInternals* about_signin_internals =
380 AboutSigninInternalsFactory::GetForProfile(Profile::FromWebUI(web_ui()));
381 about_signin_internals->OnAuthenticationResultReceived(
382 "GAIA Auth Successful");
384 content::StoragePartition* partition =
385 content::BrowserContext::GetStoragePartitionForSite(
386 contents->GetBrowserContext(), signin::GetSigninPartitionURL());
388 SigninClient* signin_client =
389 ChromeSigninClientFactory::GetForProfile(Profile::FromWebUI(web_ui()));
390 std::string signin_scoped_device_id =
391 signin_client->GetSigninScopedDeviceId();
392 // InlineSigninHelper will delete itself.
393 new InlineSigninHelper(GetWeakPtr(), partition->GetURLRequestContext(),
394 Profile::FromWebUI(web_ui()), current_url,
395 email, gaia_id, password, session_index,
396 signin_scoped_device_id, choose_what_to_sync,
397 confirm_untrusted_signin_);
399 web_ui()->CallJavascriptFunction("inline.login.closeDialog");
402 void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg) {
403 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
405 Browser* browser = GetDesktopBrowser();
406 if (browser && !error_msg.empty()) {
407 LoginUIServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
408 DisplayLoginResult(browser, base::UTF8ToUTF16(error_msg));
412 Browser* InlineLoginHandlerImpl::GetDesktopBrowser() {
413 Browser* browser = chrome::FindBrowserWithWebContents(
414 web_ui()->GetWebContents());
415 if (!browser) {
416 browser = chrome::FindLastActiveWithProfile(
417 Profile::FromWebUI(web_ui()), chrome::GetActiveDesktop());
419 return browser;
422 void InlineLoginHandlerImpl::SyncStarterCallback(
423 OneClickSigninSyncStarter::SyncSetupResult result) {
424 content::WebContents* contents = web_ui()->GetWebContents();
426 if (contents->GetController().GetPendingEntry()) {
427 // Do nothing if a navigation is pending, since this call can be triggered
428 // from DidStartLoading. This avoids deleting the pending entry while we are
429 // still navigating to it. See crbug/346632.
430 return;
433 const GURL& current_url = contents->GetLastCommittedURL();
434 signin_metrics::Source source = signin::GetSourceForPromoURL(current_url);
435 bool auto_close = signin::IsAutoCloseEnabledInURL(current_url);
437 if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) {
438 OneClickSigninHelper::RedirectToNtpOrAppsPage(contents, source);
439 } else if (auto_close) {
440 base::MessageLoop::current()->PostTask(
441 FROM_HERE,
442 base::Bind(&InlineLoginHandlerImpl::CloseTab,
443 weak_factory_.GetWeakPtr(),
444 signin::ShouldShowAccountManagement(current_url)));
445 } else {
446 OneClickSigninHelper::RedirectToNtpOrAppsPageIfNecessary(contents, source);
450 void InlineLoginHandlerImpl::CloseTab(bool show_account_management) {
451 content::WebContents* tab = web_ui()->GetWebContents();
452 Browser* browser = chrome::FindBrowserWithWebContents(tab);
453 if (browser) {
454 TabStripModel* tab_strip_model = browser->tab_strip_model();
455 if (tab_strip_model) {
456 int index = tab_strip_model->GetIndexOfWebContents(tab);
457 if (index != TabStripModel::kNoTab) {
458 tab_strip_model->ExecuteContextMenuCommand(
459 index, TabStripModel::CommandCloseTab);
463 if (show_account_management) {
464 browser->window()->ShowAvatarBubbleFromAvatarButton(
465 BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT,
466 signin::ManageAccountsParams());