// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h" #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #if defined(ENABLE_CONFIGURATION_POLICY) #include "chrome/browser/policy/cloud/user_policy_signin_service.h" #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h" #endif #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_io_data.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/sync/sync_prefs.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/webui/signin/login_ui_service.h" #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.h" #include "chrome/common/url_constants.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" OneClickSigninSyncStarter::OneClickSigninSyncStarter( Profile* profile, Browser* browser, const std::string& session_index, const std::string& email, const std::string& password, const std::string& oauth_code, StartSyncMode start_mode, content::WebContents* web_contents, ConfirmationRequired confirmation_required, Callback sync_setup_completed_callback) : content::WebContentsObserver(web_contents), start_mode_(start_mode), desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE), confirmation_required_(confirmation_required), sync_setup_completed_callback_(sync_setup_completed_callback), weak_pointer_factory_(this) { DCHECK(profile); BrowserList::AddObserver(this); Initialize(profile, browser); // If oauth_code is supplied, then start the sign in process using the // oauth_code; otherwise start the signin process using the cookies in the // cookie jar. SigninManager* manager = SigninManagerFactory::GetForProfile(profile_); SigninManager::OAuthTokenFetchedCallback callback; // Policy is enabled, so pass in a callback to do extra policy-related UI // before signin completes. callback = base::Bind(&OneClickSigninSyncStarter::ConfirmSignin, weak_pointer_factory_.GetWeakPtr()); if (oauth_code.empty()) { manager->StartSignInWithCredentials( session_index, email, password, callback); } else { manager->StartSignInWithOAuthCode(email, password, oauth_code, callback); } } void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) { if (browser == browser_) browser_ = NULL; } OneClickSigninSyncStarter::~OneClickSigninSyncStarter() { BrowserList::RemoveObserver(this); } void OneClickSigninSyncStarter::Initialize(Profile* profile, Browser* browser) { DCHECK(profile); profile_ = profile; browser_ = browser; // Cache the parent desktop for the browser, so we can reuse that same // desktop for any UI we want to display. if (browser) desktop_type_ = browser->host_desktop_type(); signin_tracker_.reset(new SigninTracker(profile_, this)); // Let the sync service know that setup is in progress so it doesn't start // syncing until the user has finished any configuration. ProfileSyncService* profile_sync_service = GetProfileSyncService(); if (profile_sync_service) profile_sync_service->SetSetupInProgress(true); // Make sure the syncing is not suppressed, otherwise the SigninManager // will not be able to complete sucessfully. browser_sync::SyncPrefs sync_prefs(profile_->GetPrefs()); sync_prefs.SetStartSuppressed(false); } void OneClickSigninSyncStarter::ConfirmSignin(const std::string& oauth_token) { DCHECK(!oauth_token.empty()); SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); // If this is a new signin (no authenticated username yet) try loading // policy for this user now, before any signed in services are initialized. if (signin->GetAuthenticatedUsername().empty()) { #if defined(ENABLE_CONFIGURATION_POLICY) policy::UserPolicySigninService* policy_service = policy::UserPolicySigninServiceFactory::GetForProfile(profile_); policy_service->RegisterForPolicy( signin->GetUsernameForAuthInProgress(), oauth_token, base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy, weak_pointer_factory_.GetWeakPtr())); return; #else ConfirmAndSignin(); #endif } else { // The user is already signed in - just tell SigninManager to continue // with its re-auth flow. signin->CompletePendingSignin(); } } #if defined(ENABLE_CONFIGURATION_POLICY) OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate( base::WeakPtr sync_starter) : sync_starter_(sync_starter) { } OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() { } void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() { if (sync_starter_ != NULL) sync_starter_->CancelSigninAndDelete(); } void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() { if (sync_starter_ != NULL) sync_starter_->LoadPolicyWithCachedCredentials(); } void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() { if (sync_starter_ != NULL) sync_starter_->CreateNewSignedInProfile(); } void OneClickSigninSyncStarter::OnRegisteredForPolicy( const std::string& dm_token, const std::string& client_id) { SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); // If there's no token for the user (policy registration did not succeed) just // finish signing in. if (dm_token.empty()) { DVLOG(1) << "Policy registration failed"; ConfirmAndSignin(); return; } DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token; // Stash away a copy of our CloudPolicyClient (should not already have one). DCHECK(dm_token_.empty()); DCHECK(client_id_.empty()); dm_token_ = dm_token; client_id_ = client_id; // Allow user to create a new profile before continuing with sign-in. EnsureBrowser(); content::WebContents* web_contents = browser_->tab_strip_model()->GetActiveWebContents(); if (!web_contents) { CancelSigninAndDelete(); return; } chrome::ShowProfileSigninConfirmationDialog( browser_, web_contents, profile_, signin->GetUsernameForAuthInProgress(), new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr())); } void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() { DCHECK(!dm_token_.empty()); DCHECK(!client_id_.empty()); SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); policy::UserPolicySigninService* policy_service = policy::UserPolicySigninServiceFactory::GetForProfile(profile_); policy_service->FetchPolicyForSignedInUser( signin->GetUsernameForAuthInProgress(), dm_token_, client_id_, base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete, weak_pointer_factory_.GetWeakPtr())); } void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success) { // For now, we allow signin to complete even if the policy fetch fails. If // we ever want to change this behavior, we could call // SigninManager::SignOut() here instead. DLOG_IF(ERROR, !success) << "Error fetching policy for user"; DVLOG_IF(1, success) << "Policy fetch successful - completing signin"; SigninManagerFactory::GetForProfile(profile_)->CompletePendingSignin(); } void OneClickSigninSyncStarter::CreateNewSignedInProfile() { SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); DCHECK(!signin->GetUsernameForAuthInProgress().empty()); DCHECK(!dm_token_.empty()); DCHECK(!client_id_.empty()); // Create a new profile and have it call back when done so we can inject our // signin credentials. size_t icon_index = g_browser_process->profile_manager()-> GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile(); ProfileManager::CreateMultiProfileAsync( UTF8ToUTF16(signin->GetUsernameForAuthInProgress()), UTF8ToUTF16(ProfileInfoCache::GetDefaultAvatarIconUrl(icon_index)), base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile, weak_pointer_factory_.GetWeakPtr(), desktop_type_), std::string()); } void OneClickSigninSyncStarter::CompleteInitForNewProfile( chrome::HostDesktopType desktop_type, Profile* new_profile, Profile::CreateStatus status) { DCHECK_NE(profile_, new_profile); // TODO(atwilson): On error, unregister the client to release the DMToken // and surface a better error for the user. switch (status) { case Profile::CREATE_STATUS_LOCAL_FAIL: { NOTREACHED() << "Error creating new profile"; CancelSigninAndDelete(); return; } case Profile::CREATE_STATUS_CREATED: { break; } case Profile::CREATE_STATUS_INITIALIZED: { // Wait until the profile is initialized before we transfer credentials. SigninManager* old_signin_manager = SigninManagerFactory::GetForProfile(profile_); SigninManager* new_signin_manager = SigninManagerFactory::GetForProfile(new_profile); DCHECK(!old_signin_manager->GetUsernameForAuthInProgress().empty()); DCHECK(old_signin_manager->GetAuthenticatedUsername().empty()); DCHECK(new_signin_manager->GetAuthenticatedUsername().empty()); DCHECK(!dm_token_.empty()); DCHECK(!client_id_.empty()); // Copy credentials from the old profile to the just-created profile, // and switch over to tracking that profile. new_signin_manager->CopyCredentialsFrom(*old_signin_manager); FinishProfileSyncServiceSetup(); Initialize(new_profile, NULL); DCHECK_EQ(profile_, new_profile); // We've transferred our credentials to the new profile - notify that // the signin for the original profile was cancelled (must do this after // we have called Initialize() with the new profile, as otherwise this // object will get freed when the signin on the old profile is cancelled. old_signin_manager->SignOut(); // Load policy for the just-created profile - once policy has finished // loading the signin process will complete. LoadPolicyWithCachedCredentials(); // Open the profile's first window, after all initialization. profiles::FindOrCreateNewWindowForProfile( new_profile, chrome::startup::IS_PROCESS_STARTUP, chrome::startup::IS_FIRST_RUN, desktop_type, false); break; } case Profile::CREATE_STATUS_REMOTE_FAIL: case Profile::CREATE_STATUS_CANCELED: case Profile::MAX_CREATE_STATUS: { NOTREACHED() << "Invalid profile creation status"; CancelSigninAndDelete(); return; } } } #endif void OneClickSigninSyncStarter::CancelSigninAndDelete() { SigninManagerFactory::GetForProfile(profile_)->SignOut(); // The statement above results in a call to SigninFailed() which will free // this object, so do not refer to the OneClickSigninSyncStarter object // after this point. } void OneClickSigninSyncStarter::ConfirmAndSignin() { SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); if (confirmation_required_ == CONFIRM_UNTRUSTED_SIGNIN) { EnsureBrowser(); // Display a confirmation dialog to the user. browser_->window()->ShowOneClickSigninBubble( BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG, UTF8ToUTF16(signin->GetUsernameForAuthInProgress()), base::string16(), // No error message to display. base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed, weak_pointer_factory_.GetWeakPtr())); } else { // No confirmation required - just sign in the user. signin->CompletePendingSignin(); } } void OneClickSigninSyncStarter::UntrustedSigninConfirmed( StartSyncMode response) { if (response == UNDO_SYNC) { CancelSigninAndDelete(); // This statement frees this object. } else { // If the user clicked the "Advanced" link in the confirmation dialog, then // override the current start_mode_ to bring up the advanced sync settings. if (response == CONFIGURE_SYNC_FIRST) start_mode_ = response; SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); signin->CompletePendingSignin(); } } void OneClickSigninSyncStarter::SigninFailed( const GoogleServiceAuthError& error) { if (!sync_setup_completed_callback_.is_null()) sync_setup_completed_callback_.Run(SYNC_SETUP_FAILURE); FinishProfileSyncServiceSetup(); if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) { switch (error.state()) { case GoogleServiceAuthError::SERVICE_UNAVAILABLE: DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16( IDS_SYNC_UNRECOVERABLE_ERROR)); break; case GoogleServiceAuthError::REQUEST_CANCELED: // No error notification needed if the user manually cancelled signin. break; default: DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16( IDS_SYNC_ERROR_SIGNING_IN)); break; } } delete this; } void OneClickSigninSyncStarter::SigninSuccess() { if (!sync_setup_completed_callback_.is_null()) sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS); switch (start_mode_) { case SYNC_WITH_DEFAULT_SETTINGS: { // Just kick off the sync machine, no need to configure it first. ProfileSyncService* profile_sync_service = GetProfileSyncService(); if (profile_sync_service) profile_sync_service->SetSyncSetupCompleted(); FinishProfileSyncServiceSetup(); if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) { base::string16 message; if (!profile_sync_service) { // Sync is disabled by policy. message = l10n_util::GetStringUTF16( IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE); } DisplayFinalConfirmationBubble(message); } break; } case CONFIGURE_SYNC_FIRST: ShowSettingsPage(true); // Show sync config UI. break; case SHOW_SETTINGS_WITHOUT_CONFIGURE: ShowSettingsPage(false); // Don't show sync config UI. break; default: NOTREACHED(); } delete this; } void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble( const base::string16& custom_message) { EnsureBrowser(); browser_->window()->ShowOneClickSigninBubble( BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE, base::string16(), // No email required - this is not a SAML confirmation. custom_message, // Callback is ignored. BrowserWindow::StartSyncCallback()); } void OneClickSigninSyncStarter::EnsureBrowser() { if (!browser_) { // The user just created a new profile or has closed the browser that // we used previously. Grab the most recently active browser or else // create a new one. browser_ = chrome::FindLastActiveWithProfile(profile_, desktop_type_); if (!browser_) { browser_ = new Browser(Browser::CreateParams(profile_, desktop_type_)); chrome::AddTabAt(browser_, GURL(), -1, true); } browser_->window()->Show(); } } void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync) { // Give the user a chance to configure things. We don't clear the // ProfileSyncService::setup_in_progress flag because we don't want sync // to start up until after the configure UI is displayed (the configure UI // will clear the flag when the user is done setting up sync). ProfileSyncService* profile_sync_service = GetProfileSyncService(); LoginUIService* login_ui = LoginUIServiceFactory::GetForProfile(profile_); if (login_ui->current_login_ui()) { login_ui->current_login_ui()->FocusUI(); } else { EnsureBrowser(); // If the sign in tab is showing a blank page and is not about to be // closed, use it to show the settings UI. bool use_same_tab = false; if (web_contents()) { GURL current_url = web_contents()->GetLastCommittedURL(); use_same_tab = signin::IsContinueUrlForWebBasedSigninFlow(current_url) && !signin::IsAutoCloseEnabledInURL(current_url); } if (profile_sync_service) { // Need to navigate to the settings page and display the sync UI. if (use_same_tab) { ShowSettingsPageInWebContents(web_contents(), chrome::kSyncSetupSubPage); } else { // If the user is setting up sync for the first time, let them configure // advanced sync settings. However, in the case of re-authentication, // return the user to the settings page without showing any config UI. if (configure_sync) { chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage); } else { FinishProfileSyncServiceSetup(); chrome::ShowSettings(browser_); } } } else { // Sync is disabled - just display the settings page. FinishProfileSyncServiceSetup(); if (use_same_tab) ShowSettingsPageInWebContents(web_contents(), std::string()); else chrome::ShowSettings(browser_); } } } ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() { ProfileSyncService* service = NULL; if (profile_->IsSyncAccessible()) service = ProfileSyncServiceFactory::GetForProfile(profile_); return service; } void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() { ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); if (service) service->SetSetupInProgress(false); } void OneClickSigninSyncStarter::ShowSettingsPageInWebContents( content::WebContents* contents, const std::string& sub_page) { std::string url = std::string(chrome::kChromeUISettingsURL) + sub_page; content::OpenURLParams params(GURL(url), content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_AUTO_TOPLEVEL, false); contents->OpenURL(params); // Activate the tab. Browser* browser = chrome::FindBrowserWithWebContents(contents); int content_index = browser->tab_strip_model()->GetIndexOfWebContents(contents); browser->tab_strip_model()->ActivateTabAt(content_index, false /* user_gesture */); }