Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / sync / one_click_signin_sync_starter.cc
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_avatar_icon_util.h"
18 #include "chrome/browser/profiles/profile_info_cache.h"
19 #include "chrome/browser/profiles/profile_io_data.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/profiles/profile_window.h"
22 #include "chrome/browser/signin/signin_manager_factory.h"
23 #include "chrome/browser/signin/signin_tracker_factory.h"
24 #include "chrome/browser/sync/profile_sync_service.h"
25 #include "chrome/browser/sync/profile_sync_service_factory.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/sync/one_click_signin_sync_observer.h"
35 #include "chrome/browser/ui/tabs/tab_strip_model.h"
36 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
37 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
38 #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.h"
39 #include "chrome/common/url_constants.h"
40 #include "components/signin/core/browser/signin_manager.h"
41 #include "components/signin/core/common/profile_management_switches.h"
42 #include "components/sync_driver/sync_prefs.h"
43 #include "grit/chromium_strings.h"
44 #include "grit/generated_resources.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h"
47
48 OneClickSigninSyncStarter::OneClickSigninSyncStarter(
49     Profile* profile,
50     Browser* browser,
51     const std::string& email,
52     const std::string& password,
53     const std::string& refresh_token,
54     StartSyncMode start_mode,
55     content::WebContents* web_contents,
56     ConfirmationRequired confirmation_required,
57     const GURL& continue_url,
58     Callback sync_setup_completed_callback)
59     : content::WebContentsObserver(web_contents),
60       start_mode_(start_mode),
61       desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
62       confirmation_required_(confirmation_required),
63       continue_url_(continue_url),
64       sync_setup_completed_callback_(sync_setup_completed_callback),
65       weak_pointer_factory_(this) {
66   DCHECK(profile);
67   DCHECK(web_contents || continue_url.is_empty());
68   BrowserList::AddObserver(this);
69
70   Initialize(profile, browser);
71
72   // Policy is enabled, so pass in a callback to do extra policy-related UI
73   // before signin completes.
74   SigninManagerFactory::GetForProfile(profile_)->
75       StartSignInWithRefreshToken(
76           refresh_token, email, password,
77           base::Bind(&OneClickSigninSyncStarter::ConfirmSignin,
78                      weak_pointer_factory_.GetWeakPtr()));
79 }
80
81 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) {
82   if (browser == browser_)
83     browser_ = NULL;
84 }
85
86 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
87   BrowserList::RemoveObserver(this);
88 }
89
90 void OneClickSigninSyncStarter::Initialize(Profile* profile, Browser* browser) {
91   DCHECK(profile);
92   profile_ = profile;
93   browser_ = browser;
94
95   // Cache the parent desktop for the browser, so we can reuse that same
96   // desktop for any UI we want to display.
97   if (browser) {
98     desktop_type_ = browser->host_desktop_type();
99   } else {
100     desktop_type_ = chrome::GetActiveDesktop();
101   }
102
103   signin_tracker_ = SigninTrackerFactory::CreateForProfile(profile_, this);
104
105   // Let the sync service know that setup is in progress so it doesn't start
106   // syncing until the user has finished any configuration.
107   ProfileSyncService* profile_sync_service = GetProfileSyncService();
108   if (profile_sync_service)
109     profile_sync_service->SetSetupInProgress(true);
110
111   // Make sure the syncing is not suppressed, otherwise the SigninManager
112   // will not be able to complete sucessfully.
113   sync_driver::SyncPrefs sync_prefs(profile_->GetPrefs());
114   sync_prefs.SetStartSuppressed(false);
115 }
116
117 void OneClickSigninSyncStarter::ConfirmSignin(const std::string& oauth_token) {
118   DCHECK(!oauth_token.empty());
119   SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
120   // If this is a new signin (no authenticated username yet) try loading
121   // policy for this user now, before any signed in services are initialized.
122   if (signin->GetAuthenticatedUsername().empty()) {
123 #if defined(ENABLE_CONFIGURATION_POLICY)
124     policy::UserPolicySigninService* policy_service =
125         policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
126     policy_service->RegisterForPolicy(
127         signin->GetUsernameForAuthInProgress(),
128         oauth_token,
129         base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy,
130                    weak_pointer_factory_.GetWeakPtr()));
131     return;
132 #else
133     ConfirmAndSignin();
134 #endif
135   } else {
136     // The user is already signed in - just tell SigninManager to continue
137     // with its re-auth flow.
138     signin->CompletePendingSignin();
139   }
140 }
141
142 #if defined(ENABLE_CONFIGURATION_POLICY)
143 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
144     base::WeakPtr<OneClickSigninSyncStarter> sync_starter)
145   : sync_starter_(sync_starter) {
146 }
147
148 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
149 }
150
151 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
152   if (sync_starter_ != NULL)
153     sync_starter_->CancelSigninAndDelete();
154 }
155
156 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
157   if (sync_starter_ != NULL)
158     sync_starter_->LoadPolicyWithCachedCredentials();
159 }
160
161 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
162   if (sync_starter_ != NULL)
163     sync_starter_->CreateNewSignedInProfile();
164 }
165
166 void OneClickSigninSyncStarter::OnRegisteredForPolicy(
167     const std::string& dm_token, const std::string& client_id) {
168   SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
169   // If there's no token for the user (policy registration did not succeed) just
170   // finish signing in.
171   if (dm_token.empty()) {
172     DVLOG(1) << "Policy registration failed";
173     ConfirmAndSignin();
174     return;
175   }
176
177   DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token;
178
179   // Stash away a copy of our CloudPolicyClient (should not already have one).
180   DCHECK(dm_token_.empty());
181   DCHECK(client_id_.empty());
182   dm_token_ = dm_token;
183   client_id_ = client_id;
184
185   // Allow user to create a new profile before continuing with sign-in.
186   browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
187   content::WebContents* web_contents =
188       browser_->tab_strip_model()->GetActiveWebContents();
189   if (!web_contents) {
190     CancelSigninAndDelete();
191     return;
192   }
193   chrome::ShowProfileSigninConfirmationDialog(
194       browser_,
195       web_contents,
196       profile_,
197       signin->GetUsernameForAuthInProgress(),
198       new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr()));
199 }
200
201 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() {
202   DCHECK(!dm_token_.empty());
203   DCHECK(!client_id_.empty());
204   SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
205   policy::UserPolicySigninService* policy_service =
206       policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
207   policy_service->FetchPolicyForSignedInUser(
208       signin->GetUsernameForAuthInProgress(),
209       dm_token_,
210       client_id_,
211       profile_->GetRequestContext(),
212       base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete,
213                  weak_pointer_factory_.GetWeakPtr()));
214 }
215
216 void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success) {
217   // For now, we allow signin to complete even if the policy fetch fails. If
218   // we ever want to change this behavior, we could call
219   // SigninManager::SignOut() here instead.
220   DLOG_IF(ERROR, !success) << "Error fetching policy for user";
221   DVLOG_IF(1, success) << "Policy fetch successful - completing signin";
222   SigninManagerFactory::GetForProfile(profile_)->CompletePendingSignin();
223 }
224
225 void OneClickSigninSyncStarter::CreateNewSignedInProfile() {
226   SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
227   DCHECK(!signin->GetUsernameForAuthInProgress().empty());
228   DCHECK(!dm_token_.empty());
229   DCHECK(!client_id_.empty());
230   // Create a new profile and have it call back when done so we can inject our
231   // signin credentials.
232   size_t icon_index = g_browser_process->profile_manager()->
233       GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile();
234   ProfileManager::CreateMultiProfileAsync(
235       base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()),
236       base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl(icon_index)),
237       base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile,
238                  weak_pointer_factory_.GetWeakPtr(), desktop_type_),
239       std::string());
240 }
241
242 void OneClickSigninSyncStarter::CompleteInitForNewProfile(
243     chrome::HostDesktopType desktop_type,
244     Profile* new_profile,
245     Profile::CreateStatus status) {
246   DCHECK_NE(profile_, new_profile);
247
248   // TODO(atwilson): On error, unregister the client to release the DMToken
249   // and surface a better error for the user.
250   switch (status) {
251     case Profile::CREATE_STATUS_LOCAL_FAIL: {
252       NOTREACHED() << "Error creating new profile";
253       CancelSigninAndDelete();
254       return;
255     }
256     case Profile::CREATE_STATUS_CREATED: {
257       break;
258     }
259     case Profile::CREATE_STATUS_INITIALIZED: {
260       // Wait until the profile is initialized before we transfer credentials.
261       SigninManager* old_signin_manager =
262           SigninManagerFactory::GetForProfile(profile_);
263       SigninManager* new_signin_manager =
264           SigninManagerFactory::GetForProfile(new_profile);
265       DCHECK(!old_signin_manager->GetUsernameForAuthInProgress().empty());
266       DCHECK(old_signin_manager->GetAuthenticatedUsername().empty());
267       DCHECK(new_signin_manager->GetAuthenticatedUsername().empty());
268       DCHECK(!dm_token_.empty());
269       DCHECK(!client_id_.empty());
270
271       // Copy credentials from the old profile to the just-created profile,
272       // and switch over to tracking that profile.
273       new_signin_manager->CopyCredentialsFrom(*old_signin_manager);
274       FinishProfileSyncServiceSetup();
275       Initialize(new_profile, NULL);
276       DCHECK_EQ(profile_, new_profile);
277
278       // We've transferred our credentials to the new profile - notify that
279       // the signin for the original profile was cancelled (must do this after
280       // we have called Initialize() with the new profile, as otherwise this
281       // object will get freed when the signin on the old profile is cancelled.
282       old_signin_manager->SignOut();
283
284       // Load policy for the just-created profile - once policy has finished
285       // loading the signin process will complete.
286       LoadPolicyWithCachedCredentials();
287
288       // Open the profile's first window, after all initialization.
289       profiles::FindOrCreateNewWindowForProfile(
290         new_profile,
291         chrome::startup::IS_PROCESS_STARTUP,
292         chrome::startup::IS_FIRST_RUN,
293         desktop_type,
294         false);
295       break;
296     }
297     case Profile::CREATE_STATUS_REMOTE_FAIL:
298     case Profile::CREATE_STATUS_CANCELED:
299     case Profile::MAX_CREATE_STATUS: {
300       NOTREACHED() << "Invalid profile creation status";
301       CancelSigninAndDelete();
302       return;
303     }
304   }
305 }
306 #endif
307
308 void OneClickSigninSyncStarter::CancelSigninAndDelete() {
309   SigninManagerFactory::GetForProfile(profile_)->SignOut();
310   // The statement above results in a call to SigninFailed() which will free
311   // this object, so do not refer to the OneClickSigninSyncStarter object
312   // after this point.
313 }
314
315 void OneClickSigninSyncStarter::ConfirmAndSignin() {
316   SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
317   if (confirmation_required_ == CONFIRM_UNTRUSTED_SIGNIN) {
318     browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
319     // Display a confirmation dialog to the user.
320     browser_->window()->ShowOneClickSigninBubble(
321         BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG,
322         base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()),
323         base::string16(),  // No error message to display.
324         base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed,
325                    weak_pointer_factory_.GetWeakPtr()));
326   } else {
327     // No confirmation required - just sign in the user.
328     signin->CompletePendingSignin();
329   }
330 }
331
332 void OneClickSigninSyncStarter::UntrustedSigninConfirmed(
333     StartSyncMode response) {
334   if (response == UNDO_SYNC) {
335     CancelSigninAndDelete();  // This statement frees this object.
336   } else {
337     // If the user clicked the "Advanced" link in the confirmation dialog, then
338     // override the current start_mode_ to bring up the advanced sync settings.
339     if (response == CONFIGURE_SYNC_FIRST)
340       start_mode_ = response;
341     SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
342     signin->CompletePendingSignin();
343   }
344 }
345
346 void OneClickSigninSyncStarter::SigninFailed(
347     const GoogleServiceAuthError& error) {
348   if (!sync_setup_completed_callback_.is_null())
349     sync_setup_completed_callback_.Run(SYNC_SETUP_FAILURE);
350
351   FinishProfileSyncServiceSetup();
352   if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) {
353     switch (error.state()) {
354       case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
355         DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
356             IDS_SYNC_UNRECOVERABLE_ERROR));
357         break;
358       case GoogleServiceAuthError::REQUEST_CANCELED:
359         // No error notification needed if the user manually cancelled signin.
360         break;
361       default:
362         DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
363             IDS_SYNC_ERROR_SIGNING_IN));
364         break;
365     }
366   }
367   delete this;
368 }
369
370 void OneClickSigninSyncStarter::SigninSuccess() {
371   if (switches::IsEnableWebBasedSignin())
372     MergeSessionComplete(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
373 }
374
375 void OneClickSigninSyncStarter::MergeSessionComplete(
376     const GoogleServiceAuthError& error) {
377   // Regardless of whether the merge session completed sucessfully or not,
378   // continue with sync starting.
379
380   if (!sync_setup_completed_callback_.is_null())
381     sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS);
382
383   switch (start_mode_) {
384     case SYNC_WITH_DEFAULT_SETTINGS: {
385       // Just kick off the sync machine, no need to configure it first.
386       ProfileSyncService* profile_sync_service = GetProfileSyncService();
387       if (profile_sync_service)
388         profile_sync_service->SetSyncSetupCompleted();
389       FinishProfileSyncServiceSetup();
390       if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) {
391         base::string16 message;
392         if (!profile_sync_service) {
393           // Sync is disabled by policy.
394           message = l10n_util::GetStringUTF16(
395               IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE);
396         }
397         DisplayFinalConfirmationBubble(message);
398       }
399       break;
400     }
401     case CONFIGURE_SYNC_FIRST:
402       ShowSettingsPage(true);  // Show sync config UI.
403       break;
404     case SHOW_SETTINGS_WITHOUT_CONFIGURE:
405       ShowSettingsPage(false);  // Don't show sync config UI.
406       break;
407     case UNDO_SYNC:
408       NOTREACHED();
409   }
410
411   // Navigate to the |continue_url_| if one is set, unless the user first needs
412   // to configure Sync.
413   if (web_contents() && !continue_url_.is_empty() &&
414       start_mode_ != CONFIGURE_SYNC_FIRST) {
415     LoadContinueUrl();
416   }
417
418   delete this;
419 }
420
421 void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble(
422     const base::string16& custom_message) {
423   browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
424   browser_->window()->ShowOneClickSigninBubble(
425       BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE,
426       base::string16(),  // No email required - this is not a SAML confirmation.
427       custom_message,
428       // Callback is ignored.
429       BrowserWindow::StartSyncCallback());
430 }
431
432 // static
433 Browser* OneClickSigninSyncStarter::EnsureBrowser(
434     Browser* browser,
435     Profile* profile,
436     chrome::HostDesktopType desktop_type) {
437   if (!browser) {
438     // The user just created a new profile or has closed the browser that
439     // we used previously. Grab the most recently active browser or else
440     // create a new one.
441     browser = chrome::FindLastActiveWithProfile(profile, desktop_type);
442     if (!browser) {
443       browser = new Browser(Browser::CreateParams(profile,
444                                                    desktop_type));
445       chrome::AddTabAt(browser, GURL(), -1, true);
446     }
447     browser->window()->Show();
448   }
449   return browser;
450 }
451
452 void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync) {
453   // Give the user a chance to configure things. We don't clear the
454   // ProfileSyncService::setup_in_progress flag because we don't want sync
455   // to start up until after the configure UI is displayed (the configure UI
456   // will clear the flag when the user is done setting up sync).
457   ProfileSyncService* profile_sync_service = GetProfileSyncService();
458   LoginUIService* login_ui = LoginUIServiceFactory::GetForProfile(profile_);
459   if (login_ui->current_login_ui()) {
460     login_ui->current_login_ui()->FocusUI();
461   } else {
462     browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
463
464     // If the sign in tab is showing the native signin page or the blank page
465     // for web-based flow, and is not about to be closed, use it to show the
466     // settings UI.
467     bool use_same_tab = false;
468     if (web_contents()) {
469       GURL current_url = web_contents()->GetLastCommittedURL();
470       bool is_chrome_signin_url =
471           current_url.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL;
472       bool is_same_profile =
473           Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ==
474           profile_;
475       use_same_tab =
476           (is_chrome_signin_url ||
477            signin::IsContinueUrlForWebBasedSigninFlow(current_url)) &&
478           !signin::IsAutoCloseEnabledInURL(current_url) &&
479           is_same_profile;
480     }
481     if (profile_sync_service) {
482       // Need to navigate to the settings page and display the sync UI.
483       if (use_same_tab) {
484         ShowSettingsPageInWebContents(web_contents(),
485                                       chrome::kSyncSetupSubPage);
486       } else {
487         // If the user is setting up sync for the first time, let them configure
488         // advanced sync settings. However, in the case of re-authentication,
489         // return the user to the settings page without showing any config UI.
490         if (configure_sync) {
491           chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
492         } else {
493           FinishProfileSyncServiceSetup();
494           chrome::ShowSettings(browser_);
495         }
496       }
497     } else {
498       // Sync is disabled - just display the settings page or redirect to the
499       // |continue_url_|.
500       FinishProfileSyncServiceSetup();
501       if (!use_same_tab)
502         chrome::ShowSettings(browser_);
503       else if (!continue_url_.is_empty())
504         LoadContinueUrl();
505       else
506         ShowSettingsPageInWebContents(web_contents(), std::string());
507     }
508   }
509 }
510
511 ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() {
512   ProfileSyncService* service = NULL;
513   if (profile_->IsSyncAccessible())
514     service = ProfileSyncServiceFactory::GetForProfile(profile_);
515   return service;
516 }
517
518 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
519   ProfileSyncService* service =
520       ProfileSyncServiceFactory::GetForProfile(profile_);
521   if (service)
522     service->SetSetupInProgress(false);
523 }
524
525 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents(
526     content::WebContents* contents,
527     const std::string& sub_page) {
528   if (!continue_url_.is_empty()) {
529     // The observer deletes itself once it's done.
530     DCHECK(!sub_page.empty());
531     new OneClickSigninSyncObserver(contents, continue_url_);
532   }
533
534   GURL url = chrome::GetSettingsUrl(sub_page);
535   content::OpenURLParams params(url,
536                                 content::Referrer(),
537                                 CURRENT_TAB,
538                                 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
539                                 false);
540   contents->OpenURL(params);
541
542   // Activate the tab.
543   Browser* browser = chrome::FindBrowserWithWebContents(contents);
544   int content_index =
545       browser->tab_strip_model()->GetIndexOfWebContents(contents);
546   browser->tab_strip_model()->ActivateTabAt(content_index,
547                                             false /* user_gesture */);
548 }
549
550 void OneClickSigninSyncStarter::LoadContinueUrl() {
551   web_contents()->GetController().LoadURL(
552       continue_url_,
553       content::Referrer(),
554       content::PAGE_TRANSITION_AUTO_TOPLEVEL,
555       std::string());
556 }