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