Show sync configuration UI in same tab as sign in flow when user refreshes sign in...
[chromium-blink-merge.git] / chrome / browser / ui / sync / one_click_signin_sync_starter.cc
blob441ebc5ab6682d78f453264207ccb53647e0f1c2
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.
5 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
7 #include "base/prefs/pref_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
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
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"
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);
64 Initialize(profile, browser);
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);
76 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) {
77 if (browser == browser_)
78 browser_ = NULL;
81 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
82 BrowserList::RemoveObserver(this);
85 void OneClickSigninSyncStarter::Initialize(Profile* profile, Browser* browser) {
86 DCHECK(profile);
87 profile_ = profile;
88 browser_ = browser;
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();
95 signin_tracker_.reset(new SigninTracker(profile_, this));
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);
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);
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();
137 #if defined(ENABLE_CONFIGURATION_POLICY)
138 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
139 base::WeakPtr<OneClickSigninSyncStarter> sync_starter)
140 : sync_starter_(sync_starter) {
143 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
146 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
147 sync_starter_->CancelSigninAndDelete();
150 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
151 sync_starter_->LoadPolicyWithCachedClient();
154 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
155 sync_starter_->CreateNewSignedInProfile();
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;
169 DCHECK(client->is_registered());
170 DVLOG(1) << "Policy registration succeeded: dm_token=" << client->dm_token();
172 // Stash away a copy of our CloudPolicyClient (should not already have one).
173 DCHECK(!policy_client_);
174 policy_client_.swap(client);
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;
184 chrome::ShowProfileSigninConfirmationDialog(
185 browser_,
186 web_contents,
187 profile_,
188 signin->GetUsernameForAuthInProgress(),
189 new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr()));
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.
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()));
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();
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());
234 void OneClickSigninSyncStarter::CompleteInitForNewProfile(
235 chrome::HostDesktopType desktop_type,
236 Profile* new_profile,
237 Profile::CreateStatus status) {
238 DCHECK_NE(profile_, new_profile);
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;
248 case Profile::CREATE_STATUS_CREATED: {
249 break;
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_);
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);
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();
275 // Load policy for the just-created profile - once policy has finished
276 // loading the signin process will complete.
277 LoadPolicyWithCachedClient();
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;
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;
297 #endif
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();
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();
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);
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;
351 delete this;
354 void OneClickSigninSyncStarter::SigninSuccess() {
355 if (!sync_setup_completed_callback_.is_null())
356 sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS);
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);
372 DisplayFinalConfirmationBubble(message);
374 break;
376 case CONFIGURE_SYNC_FIRST:
377 ShowSettingsPage(true); // Show sync config UI.
378 break;
379 case SHOW_SETTINGS_WITHOUT_CONFIGURE:
380 ShowSettingsPage(false); // Don't show sync config UI.
381 break;
382 default:
383 NOTREACHED();
385 delete this;
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());
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);
410 browser_->window()->Show();
414 void OneClickSigninSyncStarter::ShowSettingsPage(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();
426 // If the sign in tab is showing a blank page and is not about to be
427 // closed, use it to show the settings UI.
428 bool use_same_tab = false;
429 if (web_contents()) {
430 GURL current_url = web_contents()->GetLastCommittedURL();
431 use_same_tab = signin::IsContinueUrlForWebBasedSigninFlow(current_url) &&
432 !signin::IsAutoCloseEnabledInURL(current_url);
434 if (profile_sync_service) {
435 // Need to navigate to the settings page and display the sync UI.
436 if (use_same_tab) {
437 ShowSettingsPageInWebContents(web_contents(),
438 chrome::kSyncSetupSubPage);
439 } else {
440 // If the user is setting up sync for the first time, let them configure
441 // advanced sync settings. However, in the case of re-authentication,
442 // return the user to the settings page without showing any config UI.
443 if (configure_sync) {
444 chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
445 } else {
446 FinishProfileSyncServiceSetup();
447 chrome::ShowSettings(browser_);
450 } else {
451 // Sync is disabled - just display the settings page.
452 FinishProfileSyncServiceSetup();
453 if (use_same_tab)
454 ShowSettingsPageInWebContents(web_contents(), std::string());
455 else
456 chrome::ShowSettings(browser_);
461 ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() {
462 ProfileSyncService* service = NULL;
463 if (profile_->IsSyncAccessible())
464 service = ProfileSyncServiceFactory::GetForProfile(profile_);
465 return service;
468 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
469 ProfileSyncService* service =
470 ProfileSyncServiceFactory::GetForProfile(profile_);
471 if (service)
472 service->SetSetupInProgress(false);
475 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents(
476 content::WebContents* contents,
477 const std::string& sub_page) {
478 std::string url = std::string(chrome::kChromeUISettingsURL) + sub_page;
479 content::OpenURLParams params(GURL(url),
480 content::Referrer(),
481 CURRENT_TAB,
482 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
483 false);
484 contents->OpenURL(params);
486 // Activate the tab.
487 Browser* browser = chrome::FindBrowserWithWebContents(contents);
488 int content_index =
489 browser->tab_strip_model()->GetIndexOfWebContents(contents);
490 browser->tab_strip_model()->ActivateTabAt(content_index,
491 false /* user_gesture */);