one_click_signin_sync_starter.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
44OneClickSigninSyncStarter::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
83void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) {
84  if (browser == browser_)
85    browser_ = NULL;
86}
87
88OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
89  BrowserList::RemoveObserver(this);
90}
91
92void 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
116void 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)
142OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
143    base::WeakPtr<OneClickSigninSyncStarter> sync_starter)
144  : sync_starter_(sync_starter) {
145}
146
147OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
148}
149
150void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
151  if (sync_starter_ != NULL)
152    sync_starter_->CancelSigninAndDelete();
153}
154
155void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
156  if (sync_starter_ != NULL)
157    sync_starter_->LoadPolicyWithCachedCredentials();
158}
159
160void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
161  if (sync_starter_ != NULL)
162    sync_starter_->CreateNewSignedInProfile();
163}
164
165void 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
200void 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
214void 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
223void 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
240void 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
306void 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
313void 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        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
330void 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
344void 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
368void 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        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
402void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble(
403    const string16& custom_message) {
404  EnsureBrowser();
405  browser_->window()->ShowOneClickSigninBubble(
406      BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE,
407      string16(),  // No email required - this is not a SAML confirmation.
408      custom_message,
409      // Callback is ignored.
410      BrowserWindow::StartSyncCallback());
411}
412
413void 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
428void 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
475ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() {
476  ProfileSyncService* service = NULL;
477  if (profile_->IsSyncAccessible())
478    service = ProfileSyncServiceFactory::GetForProfile(profile_);
479  return service;
480}
481
482void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
483  ProfileSyncService* service =
484      ProfileSyncServiceFactory::GetForProfile(profile_);
485  if (service)
486    service->SetSetupInProgress(false);
487}
488
489void 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