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     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