Home | History | Annotate | Download | only in sync
      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