root/chrome/browser/ui/sync/one_click_signin_sync_starter.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_pointer_factory_
  2. OnBrowserRemoved
  3. Initialize
  4. ConfirmSignin
  5. OnCancelSignin
  6. OnContinueSignin
  7. OnSigninWithNewProfile
  8. OnRegisteredForPolicy
  9. LoadPolicyWithCachedCredentials
  10. OnPolicyFetchComplete
  11. CreateNewSignedInProfile
  12. CompleteInitForNewProfile
  13. CancelSigninAndDelete
  14. ConfirmAndSignin
  15. UntrustedSigninConfirmed
  16. SigninFailed
  17. SigninSuccess
  18. MergeSessionComplete
  19. DisplayFinalConfirmationBubble
  20. EnsureBrowser
  21. ShowSettingsPage
  22. GetProfileSyncService
  23. FinishProfileSyncServiceSetup
  24. ShowSettingsPageInWebContents

// 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_factory.h"
#include "chrome/browser/signin/signin_tracker_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.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/profile_management_switches.h"
#include "chrome/common/url_constants.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/sync_driver/sync_prefs.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& email,
    const std::string& password,
    const std::string& refresh_token,
    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);

  // Policy is enabled, so pass in a callback to do extra policy-related UI
  // before signin completes.
  SigninManagerFactory::GetForProfile(profile_)->
      StartSignInWithRefreshToken(
          refresh_token, email, password,
          base::Bind(&OneClickSigninSyncStarter::ConfirmSignin,
                     weak_pointer_factory_.GetWeakPtr()));
}

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();
  } else {
    desktop_type_ = chrome::GetActiveDesktop();
  }

  signin_tracker_ = SigninTrackerFactory::CreateForProfile(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.
  sync_driver::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<OneClickSigninSyncStarter> 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.
  browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
  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_,
      profile_->GetRequestContext(),
      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(
      base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()),
      base::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) {
    browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
    // Display a confirmation dialog to the user.
    browser_->window()->ShowOneClickSigninBubble(
        BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG,
        base::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 (switches::IsEnableWebBasedSignin())
    MergeSessionComplete(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
}

void OneClickSigninSyncStarter::MergeSessionComplete(
    const GoogleServiceAuthError& error) {
  // Regardless of whether the merge session completed sucessfully or not,
  // continue with sync starting.

  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) {
  browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
  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());
}

// static
Browser* OneClickSigninSyncStarter::EnsureBrowser(
    Browser* browser,
    Profile* profile,
    chrome::HostDesktopType desktop_type) {
  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();
  }
  return browser;
}

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 {
    browser_ = EnsureBrowser(browser_, profile_, desktop_type_);

    // If the sign in tab is showing the native signin page or the blank page
    // for web-based flow, 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();
      bool is_chrome_signin_url =
          current_url.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL;
      bool is_same_profile =
          Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ==
          profile_;
      use_same_tab =
          (is_chrome_signin_url ||
           signin::IsContinueUrlForWebBasedSigninFlow(current_url)) &&
          !signin::IsAutoCloseEnabledInURL(current_url) &&
          is_same_profile;
    }
    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) {
  GURL url = chrome::GetSettingsUrl(sub_page);
  content::OpenURLParams params(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 */);
}

/* [<][>][^][v][top][bottom][index][help] */