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