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/ui/sync/one_click_signin_sync_starter.h" 6 7 #include "base/prefs/pref_service.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "chrome/browser/browser_process.h" 10 11 #if defined(ENABLE_CONFIGURATION_POLICY) 12 #include "chrome/browser/policy/cloud/user_policy_signin_service.h" 13 #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h" 14 #endif 15 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/profiles/profile_avatar_icon_util.h" 18 #include "chrome/browser/profiles/profile_info_cache.h" 19 #include "chrome/browser/profiles/profile_io_data.h" 20 #include "chrome/browser/profiles/profile_manager.h" 21 #include "chrome/browser/profiles/profile_window.h" 22 #include "chrome/browser/signin/signin_manager_factory.h" 23 #include "chrome/browser/signin/signin_tracker_factory.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.h" 27 #include "chrome/browser/ui/browser_dialogs.h" 28 #include "chrome/browser/ui/browser_finder.h" 29 #include "chrome/browser/ui/browser_list.h" 30 #include "chrome/browser/ui/browser_navigator.h" 31 #include "chrome/browser/ui/browser_tabstrip.h" 32 #include "chrome/browser/ui/browser_window.h" 33 #include "chrome/browser/ui/chrome_pages.h" 34 #include "chrome/browser/ui/sync/one_click_signin_sync_observer.h" 35 #include "chrome/browser/ui/tabs/tab_strip_model.h" 36 #include "chrome/browser/ui/webui/signin/login_ui_service.h" 37 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" 38 #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.h" 39 #include "chrome/common/url_constants.h" 40 #include "components/signin/core/browser/signin_manager.h" 41 #include "components/signin/core/browser/signin_metrics.h" 42 #include "components/signin/core/common/profile_management_switches.h" 43 #include "components/sync_driver/sync_prefs.h" 44 #include "grit/chromium_strings.h" 45 #include "grit/generated_resources.h" 46 #include "ui/base/l10n/l10n_util.h" 47 #include "ui/base/resource/resource_bundle.h" 48 49 OneClickSigninSyncStarter::OneClickSigninSyncStarter( 50 Profile* profile, 51 Browser* browser, 52 const std::string& email, 53 const std::string& password, 54 const std::string& refresh_token, 55 StartSyncMode start_mode, 56 content::WebContents* web_contents, 57 ConfirmationRequired confirmation_required, 58 const GURL& continue_url, 59 Callback sync_setup_completed_callback) 60 : content::WebContentsObserver(web_contents), 61 start_mode_(start_mode), 62 desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE), 63 confirmation_required_(confirmation_required), 64 continue_url_(continue_url), 65 sync_setup_completed_callback_(sync_setup_completed_callback), 66 weak_pointer_factory_(this) { 67 DCHECK(profile); 68 DCHECK(web_contents || continue_url.is_empty()); 69 BrowserList::AddObserver(this); 70 71 Initialize(profile, browser); 72 73 // Policy is enabled, so pass in a callback to do extra policy-related UI 74 // before signin completes. 75 SigninManagerFactory::GetForProfile(profile_)-> 76 StartSignInWithRefreshToken( 77 refresh_token, email, password, 78 base::Bind(&OneClickSigninSyncStarter::ConfirmSignin, 79 weak_pointer_factory_.GetWeakPtr())); 80 } 81 82 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) { 83 if (browser == browser_) 84 browser_ = NULL; 85 } 86 87 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() { 88 BrowserList::RemoveObserver(this); 89 } 90 91 void OneClickSigninSyncStarter::Initialize(Profile* profile, Browser* browser) { 92 DCHECK(profile); 93 profile_ = profile; 94 browser_ = browser; 95 96 // Cache the parent desktop for the browser, so we can reuse that same 97 // desktop for any UI we want to display. 98 if (browser) { 99 desktop_type_ = browser->host_desktop_type(); 100 } else { 101 desktop_type_ = chrome::GetActiveDesktop(); 102 } 103 104 signin_tracker_ = SigninTrackerFactory::CreateForProfile(profile_, this); 105 106 // Let the sync service know that setup is in progress so it doesn't start 107 // syncing until the user has finished any configuration. 108 ProfileSyncService* profile_sync_service = GetProfileSyncService(); 109 if (profile_sync_service) 110 profile_sync_service->SetSetupInProgress(true); 111 112 // Make sure the syncing is not suppressed, otherwise the SigninManager 113 // will not be able to complete sucessfully. 114 sync_driver::SyncPrefs sync_prefs(profile_->GetPrefs()); 115 sync_prefs.SetStartSuppressed(false); 116 } 117 118 void OneClickSigninSyncStarter::ConfirmSignin(const std::string& oauth_token) { 119 DCHECK(!oauth_token.empty()); 120 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 121 // If this is a new signin (no authenticated username yet) try loading 122 // policy for this user now, before any signed in services are initialized. 123 if (signin->GetAuthenticatedUsername().empty()) { 124 #if defined(ENABLE_CONFIGURATION_POLICY) 125 policy::UserPolicySigninService* policy_service = 126 policy::UserPolicySigninServiceFactory::GetForProfile(profile_); 127 policy_service->RegisterForPolicy( 128 signin->GetUsernameForAuthInProgress(), 129 oauth_token, 130 base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy, 131 weak_pointer_factory_.GetWeakPtr())); 132 return; 133 #else 134 ConfirmAndSignin(); 135 #endif 136 } else { 137 // The user is already signed in - just tell SigninManager to continue 138 // with its re-auth flow. 139 signin->CompletePendingSignin(); 140 } 141 } 142 143 #if defined(ENABLE_CONFIGURATION_POLICY) 144 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate( 145 base::WeakPtr<OneClickSigninSyncStarter> sync_starter) 146 : sync_starter_(sync_starter) { 147 } 148 149 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() { 150 } 151 152 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() { 153 if (sync_starter_ != NULL) 154 sync_starter_->CancelSigninAndDelete(); 155 } 156 157 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() { 158 if (sync_starter_ != NULL) 159 sync_starter_->LoadPolicyWithCachedCredentials(); 160 } 161 162 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() { 163 if (sync_starter_ != NULL) 164 sync_starter_->CreateNewSignedInProfile(); 165 } 166 167 void OneClickSigninSyncStarter::OnRegisteredForPolicy( 168 const std::string& dm_token, const std::string& client_id) { 169 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 170 // If there's no token for the user (policy registration did not succeed) just 171 // finish signing in. 172 if (dm_token.empty()) { 173 DVLOG(1) << "Policy registration failed"; 174 ConfirmAndSignin(); 175 return; 176 } 177 178 DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token; 179 180 // Stash away a copy of our CloudPolicyClient (should not already have one). 181 DCHECK(dm_token_.empty()); 182 DCHECK(client_id_.empty()); 183 dm_token_ = dm_token; 184 client_id_ = client_id; 185 186 // Allow user to create a new profile before continuing with sign-in. 187 browser_ = EnsureBrowser(browser_, profile_, desktop_type_); 188 content::WebContents* web_contents = 189 browser_->tab_strip_model()->GetActiveWebContents(); 190 if (!web_contents) { 191 CancelSigninAndDelete(); 192 return; 193 } 194 chrome::ShowProfileSigninConfirmationDialog( 195 browser_, 196 web_contents, 197 profile_, 198 signin->GetUsernameForAuthInProgress(), 199 new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr())); 200 } 201 202 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() { 203 DCHECK(!dm_token_.empty()); 204 DCHECK(!client_id_.empty()); 205 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 206 policy::UserPolicySigninService* policy_service = 207 policy::UserPolicySigninServiceFactory::GetForProfile(profile_); 208 policy_service->FetchPolicyForSignedInUser( 209 signin->GetUsernameForAuthInProgress(), 210 dm_token_, 211 client_id_, 212 profile_->GetRequestContext(), 213 base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete, 214 weak_pointer_factory_.GetWeakPtr())); 215 } 216 217 void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success) { 218 // For now, we allow signin to complete even if the policy fetch fails. If 219 // we ever want to change this behavior, we could call 220 // SigninManager::SignOut() here instead. 221 DLOG_IF(ERROR, !success) << "Error fetching policy for user"; 222 DVLOG_IF(1, success) << "Policy fetch successful - completing signin"; 223 SigninManagerFactory::GetForProfile(profile_)->CompletePendingSignin(); 224 } 225 226 void OneClickSigninSyncStarter::CreateNewSignedInProfile() { 227 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 228 DCHECK(!signin->GetUsernameForAuthInProgress().empty()); 229 DCHECK(!dm_token_.empty()); 230 DCHECK(!client_id_.empty()); 231 // Create a new profile and have it call back when done so we can inject our 232 // signin credentials. 233 size_t icon_index = g_browser_process->profile_manager()-> 234 GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile(); 235 ProfileManager::CreateMultiProfileAsync( 236 base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()), 237 base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl(icon_index)), 238 base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile, 239 weak_pointer_factory_.GetWeakPtr(), desktop_type_), 240 std::string()); 241 } 242 243 void OneClickSigninSyncStarter::CompleteInitForNewProfile( 244 chrome::HostDesktopType desktop_type, 245 Profile* new_profile, 246 Profile::CreateStatus status) { 247 DCHECK_NE(profile_, new_profile); 248 249 // TODO(atwilson): On error, unregister the client to release the DMToken 250 // and surface a better error for the user. 251 switch (status) { 252 case Profile::CREATE_STATUS_LOCAL_FAIL: { 253 NOTREACHED() << "Error creating new profile"; 254 CancelSigninAndDelete(); 255 return; 256 } 257 case Profile::CREATE_STATUS_CREATED: { 258 break; 259 } 260 case Profile::CREATE_STATUS_INITIALIZED: { 261 // Wait until the profile is initialized before we transfer credentials. 262 SigninManager* old_signin_manager = 263 SigninManagerFactory::GetForProfile(profile_); 264 SigninManager* new_signin_manager = 265 SigninManagerFactory::GetForProfile(new_profile); 266 DCHECK(!old_signin_manager->GetUsernameForAuthInProgress().empty()); 267 DCHECK(old_signin_manager->GetAuthenticatedUsername().empty()); 268 DCHECK(new_signin_manager->GetAuthenticatedUsername().empty()); 269 DCHECK(!dm_token_.empty()); 270 DCHECK(!client_id_.empty()); 271 272 // Copy credentials from the old profile to the just-created profile, 273 // and switch over to tracking that profile. 274 new_signin_manager->CopyCredentialsFrom(*old_signin_manager); 275 FinishProfileSyncServiceSetup(); 276 Initialize(new_profile, NULL); 277 DCHECK_EQ(profile_, new_profile); 278 279 // We've transferred our credentials to the new profile - notify that 280 // the signin for the original profile was cancelled (must do this after 281 // we have called Initialize() with the new profile, as otherwise this 282 // object will get freed when the signin on the old profile is cancelled. 283 old_signin_manager->SignOut(signin_metrics::TRANSFER_CREDENTIALS); 284 285 // Load policy for the just-created profile - once policy has finished 286 // loading the signin process will complete. 287 LoadPolicyWithCachedCredentials(); 288 289 // Open the profile's first window, after all initialization. 290 profiles::FindOrCreateNewWindowForProfile( 291 new_profile, 292 chrome::startup::IS_PROCESS_STARTUP, 293 chrome::startup::IS_FIRST_RUN, 294 desktop_type, 295 false); 296 break; 297 } 298 case Profile::CREATE_STATUS_REMOTE_FAIL: 299 case Profile::CREATE_STATUS_CANCELED: 300 case Profile::MAX_CREATE_STATUS: { 301 NOTREACHED() << "Invalid profile creation status"; 302 CancelSigninAndDelete(); 303 return; 304 } 305 } 306 } 307 #endif 308 309 void OneClickSigninSyncStarter::CancelSigninAndDelete() { 310 SigninManagerFactory::GetForProfile(profile_)->SignOut( 311 signin_metrics::ABORT_SIGNIN); 312 // The statement above results in a call to SigninFailed() which will free 313 // this object, so do not refer to the OneClickSigninSyncStarter object 314 // after this point. 315 } 316 317 void OneClickSigninSyncStarter::ConfirmAndSignin() { 318 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 319 if (confirmation_required_ == CONFIRM_UNTRUSTED_SIGNIN) { 320 browser_ = EnsureBrowser(browser_, profile_, desktop_type_); 321 // Display a confirmation dialog to the user. 322 browser_->window()->ShowOneClickSigninBubble( 323 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG, 324 base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()), 325 base::string16(), // No error message to display. 326 base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed, 327 weak_pointer_factory_.GetWeakPtr())); 328 } else { 329 // No confirmation required - just sign in the user. 330 signin->CompletePendingSignin(); 331 } 332 } 333 334 void OneClickSigninSyncStarter::UntrustedSigninConfirmed( 335 StartSyncMode response) { 336 if (response == UNDO_SYNC) { 337 CancelSigninAndDelete(); // This statement frees this object. 338 } else { 339 // If the user clicked the "Advanced" link in the confirmation dialog, then 340 // override the current start_mode_ to bring up the advanced sync settings. 341 if (response == CONFIGURE_SYNC_FIRST) 342 start_mode_ = response; 343 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); 344 signin->CompletePendingSignin(); 345 } 346 } 347 348 void OneClickSigninSyncStarter::SigninFailed( 349 const GoogleServiceAuthError& error) { 350 if (!sync_setup_completed_callback_.is_null()) 351 sync_setup_completed_callback_.Run(SYNC_SETUP_FAILURE); 352 353 FinishProfileSyncServiceSetup(); 354 if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) { 355 switch (error.state()) { 356 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: 357 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16( 358 IDS_SYNC_UNRECOVERABLE_ERROR)); 359 break; 360 case GoogleServiceAuthError::REQUEST_CANCELED: 361 // No error notification needed if the user manually cancelled signin. 362 break; 363 default: 364 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16( 365 IDS_SYNC_ERROR_SIGNING_IN)); 366 break; 367 } 368 } 369 delete this; 370 } 371 372 void OneClickSigninSyncStarter::SigninSuccess() { 373 if (switches::IsEnableWebBasedSignin()) 374 MergeSessionComplete(GoogleServiceAuthError(GoogleServiceAuthError::NONE)); 375 } 376 377 void OneClickSigninSyncStarter::MergeSessionComplete( 378 const GoogleServiceAuthError& error) { 379 // Regardless of whether the merge session completed sucessfully or not, 380 // continue with sync starting. 381 382 if (!sync_setup_completed_callback_.is_null()) 383 sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS); 384 385 switch (start_mode_) { 386 case SYNC_WITH_DEFAULT_SETTINGS: { 387 // Just kick off the sync machine, no need to configure it first. 388 ProfileSyncService* profile_sync_service = GetProfileSyncService(); 389 if (profile_sync_service) 390 profile_sync_service->SetSyncSetupCompleted(); 391 FinishProfileSyncServiceSetup(); 392 if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) { 393 base::string16 message; 394 if (!profile_sync_service) { 395 // Sync is disabled by policy. 396 message = l10n_util::GetStringUTF16( 397 IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE); 398 } 399 DisplayFinalConfirmationBubble(message); 400 } 401 break; 402 } 403 case CONFIGURE_SYNC_FIRST: 404 ShowSettingsPage(true); // Show sync config UI. 405 break; 406 case SHOW_SETTINGS_WITHOUT_CONFIGURE: 407 ShowSettingsPage(false); // Don't show sync config UI. 408 break; 409 case UNDO_SYNC: 410 NOTREACHED(); 411 } 412 413 // Navigate to the |continue_url_| if one is set, unless the user first needs 414 // to configure Sync. 415 if (web_contents() && !continue_url_.is_empty() && 416 start_mode_ != CONFIGURE_SYNC_FIRST) { 417 LoadContinueUrl(); 418 } 419 420 delete this; 421 } 422 423 void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble( 424 const base::string16& custom_message) { 425 browser_ = EnsureBrowser(browser_, profile_, desktop_type_); 426 browser_->window()->ShowOneClickSigninBubble( 427 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE, 428 base::string16(), // No email required - this is not a SAML confirmation. 429 custom_message, 430 // Callback is ignored. 431 BrowserWindow::StartSyncCallback()); 432 } 433 434 // static 435 Browser* OneClickSigninSyncStarter::EnsureBrowser( 436 Browser* browser, 437 Profile* profile, 438 chrome::HostDesktopType desktop_type) { 439 if (!browser) { 440 // The user just created a new profile or has closed the browser that 441 // we used previously. Grab the most recently active browser or else 442 // create a new one. 443 browser = chrome::FindLastActiveWithProfile(profile, desktop_type); 444 if (!browser) { 445 browser = new Browser(Browser::CreateParams(profile, 446 desktop_type)); 447 chrome::AddTabAt(browser, GURL(), -1, true); 448 } 449 browser->window()->Show(); 450 } 451 return browser; 452 } 453 454 void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync) { 455 // Give the user a chance to configure things. We don't clear the 456 // ProfileSyncService::setup_in_progress flag because we don't want sync 457 // to start up until after the configure UI is displayed (the configure UI 458 // will clear the flag when the user is done setting up sync). 459 ProfileSyncService* profile_sync_service = GetProfileSyncService(); 460 LoginUIService* login_ui = LoginUIServiceFactory::GetForProfile(profile_); 461 if (login_ui->current_login_ui()) { 462 login_ui->current_login_ui()->FocusUI(); 463 } else { 464 browser_ = EnsureBrowser(browser_, profile_, desktop_type_); 465 466 // If the sign in tab is showing the native signin page or the blank page 467 // for web-based flow, and is not about to be closed, use it to show the 468 // settings UI. 469 bool use_same_tab = false; 470 if (web_contents()) { 471 GURL current_url = web_contents()->GetLastCommittedURL(); 472 bool is_chrome_signin_url = 473 current_url.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL; 474 bool is_same_profile = 475 Profile::FromBrowserContext(web_contents()->GetBrowserContext()) == 476 profile_; 477 use_same_tab = 478 (is_chrome_signin_url || 479 signin::IsContinueUrlForWebBasedSigninFlow(current_url)) && 480 !signin::IsAutoCloseEnabledInURL(current_url) && 481 is_same_profile; 482 } 483 if (profile_sync_service) { 484 // Need to navigate to the settings page and display the sync UI. 485 if (use_same_tab) { 486 ShowSettingsPageInWebContents(web_contents(), 487 chrome::kSyncSetupSubPage); 488 } else { 489 // If the user is setting up sync for the first time, let them configure 490 // advanced sync settings. However, in the case of re-authentication, 491 // return the user to the settings page without showing any config UI. 492 if (configure_sync) { 493 chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage); 494 } else { 495 FinishProfileSyncServiceSetup(); 496 chrome::ShowSettings(browser_); 497 } 498 } 499 } else { 500 // Sync is disabled - just display the settings page or redirect to the 501 // |continue_url_|. 502 FinishProfileSyncServiceSetup(); 503 if (!use_same_tab) 504 chrome::ShowSettings(browser_); 505 else if (!continue_url_.is_empty()) 506 LoadContinueUrl(); 507 else 508 ShowSettingsPageInWebContents(web_contents(), std::string()); 509 } 510 } 511 } 512 513 ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() { 514 ProfileSyncService* service = NULL; 515 if (profile_->IsSyncAccessible()) 516 service = ProfileSyncServiceFactory::GetForProfile(profile_); 517 return service; 518 } 519 520 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() { 521 ProfileSyncService* service = 522 ProfileSyncServiceFactory::GetForProfile(profile_); 523 if (service) 524 service->SetSetupInProgress(false); 525 } 526 527 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents( 528 content::WebContents* contents, 529 const std::string& sub_page) { 530 if (!continue_url_.is_empty()) { 531 // The observer deletes itself once it's done. 532 DCHECK(!sub_page.empty()); 533 new OneClickSigninSyncObserver(contents, continue_url_); 534 } 535 536 GURL url = chrome::GetSettingsUrl(sub_page); 537 content::OpenURLParams params(url, 538 content::Referrer(), 539 CURRENT_TAB, 540 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 541 false); 542 contents->OpenURL(params); 543 544 // Activate the tab. 545 Browser* browser = chrome::FindBrowserWithWebContents(contents); 546 int content_index = 547 browser->tab_strip_model()->GetIndexOfWebContents(contents); 548 browser->tab_strip_model()->ActivateTabAt(content_index, 549 false /* user_gesture */); 550 } 551 552 void OneClickSigninSyncStarter::LoadContinueUrl() { 553 web_contents()->GetController().LoadURL( 554 continue_url_, 555 content::Referrer(), 556 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 557 std::string()); 558 } 559