diff options
Diffstat (limited to 'chrome')
81 files changed, 1314 insertions, 2035 deletions
diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index 9ec92d02..c371880 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -181,6 +181,10 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, token_service->StartFetchingTokens(); } + // Set the CrOS user by getting this constructor run with the + // user's email on first retrieval. + profile->GetProfileSyncService(username); + // Attempt to take ownership; this will fail if device is already owned. OwnershipService::GetSharedInstance()->StartTakeOwnershipAttempt( UserManager::Get()->logged_in_user().email()); diff --git a/chrome/browser/cocoa/preferences_window_controller.mm b/chrome/browser/cocoa/preferences_window_controller.mm index a2ab751..58df847 100644 --- a/chrome/browser/cocoa/preferences_window_controller.mm +++ b/chrome/browser/cocoa/preferences_window_controller.mm @@ -1350,7 +1350,7 @@ const int kDisabledIndex = 1; } else { // Otherwise, the sync button was a "sync my bookmarks" button. // Kick off the sync setup process. - syncService_->EnableForUser(NULL); + syncService_->ShowLoginDialog(NULL); ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_OPTIONS); } } diff --git a/chrome/browser/dom_ui/new_tab_page_sync_handler.cc b/chrome/browser/dom_ui/new_tab_page_sync_handler.cc index 6970816..3ecd267 100644 --- a/chrome/browser/dom_ui/new_tab_page_sync_handler.cc +++ b/chrome/browser/dom_ui/new_tab_page_sync_handler.cc @@ -168,7 +168,7 @@ void NewTabPageSyncHandler::HandleSyncLinkClicked(const ListValue* args) { } else { // User clicked the 'Start now' link to begin syncing. ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_NTP); - sync_service_->EnableForUser(NULL); + sync_service_->ShowLoginDialog(NULL); } } diff --git a/chrome/browser/gtk/options/content_page_gtk.cc b/chrome/browser/gtk/options/content_page_gtk.cc index 13b2cea..9d6d72b 100644 --- a/chrome/browser/gtk/options/content_page_gtk.cc +++ b/chrome/browser/gtk/options/content_page_gtk.cc @@ -595,7 +595,7 @@ void ContentPageGtk::OnSyncStartStopButtonClicked(GtkWidget* widget) { gtk_util::ShowDialog(dialog); return; } else { - sync_service_->EnableForUser(NULL); + sync_service_->ShowLoginDialog(NULL); ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_OPTIONS); } } diff --git a/chrome/browser/net/gaia/token_service.cc b/chrome/browser/net/gaia/token_service.cc index b46868f..b61ab62 100644 --- a/chrome/browser/net/gaia/token_service.cc +++ b/chrome/browser/net/gaia/token_service.cc @@ -13,6 +13,8 @@ #include "chrome/common/notification_service.h" // Unfortunately kNumServices must be defined in the .h. +// TODO(chron): Sync doesn't use the TalkToken anymore so we can stop +// requesting it. const char* TokenService::kServices[] = {GaiaConstants::kSyncService, GaiaConstants::kTalkService}; TokenService::TokenService() @@ -29,12 +31,18 @@ void TokenService::Initialize(const char* const source, Profile* profile) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - + if (!source_.empty()) { + // Already initialized. + return; + } getter_ = profile->GetRequestContext(); // Since the user can create a bookmark in incognito, sync may be running. // Thus we have to go for explicit access. web_data_service_ = profile->GetWebDataService(Profile::EXPLICIT_ACCESS); source_ = std::string(source); + registrar_.Add(this, + NotificationType::TOKEN_UPDATED, + NotificationService::AllSources()); } void TokenService::ResetCredentialsInMemory() { @@ -147,6 +155,12 @@ void TokenService::FireTokenRequestFailedNotification( Details<const TokenRequestFailedDetails>(&details)); } +void TokenService::IssueAuthTokenForTest(const std::string& service, + const std::string& auth_token) { + token_map_[service] = auth_token; + FireTokenAvailableNotification(service, auth_token); +} + void TokenService::OnIssueAuthTokenSuccess(const std::string& service, const std::string& auth_token) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); @@ -178,6 +192,11 @@ void TokenService::OnWebDataServiceRequestDone(WebDataService::Handle h, result); LoadTokensIntoMemory(token_result->GetValue(), &token_map_); } + + NotificationService::current()->Notify( + NotificationType::TOKEN_LOADING_FINISHED, + Source<TokenService>(this), + NotificationService::NoDetails()); } // Load tokens from the db_token map into the in memory token map. @@ -207,3 +226,12 @@ void TokenService::LoadTokensIntoMemory( } } } + +void TokenService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TOKEN_UPDATED); + TokenAvailableDetails* tok_details = + Details<TokenAvailableDetails>(details).ptr(); + OnIssueAuthTokenSuccess(tok_details->service(), tok_details->token()); +} diff --git a/chrome/browser/net/gaia/token_service.h b/chrome/browser/net/gaia/token_service.h index 20ca12d..006423f 100644 --- a/chrome/browser/net/gaia/token_service.h +++ b/chrome/browser/net/gaia/token_service.h @@ -37,12 +37,15 @@ #include <map> #include <string> + +#include "base/gtest_prod_util.h" #include "base/scoped_ptr.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" #include "chrome/common/net/gaia/gaia_authenticator2.h" #include "chrome/common/net/gaia/google_service_auth_error.h" -#include "base/gtest_prod_util.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" class URLRequestContextGetter; class Profile; @@ -50,7 +53,8 @@ class Profile; // The TokenService is a Profile member, so all calls are expected // from the UI thread. class TokenService : public GaiaAuthConsumer, - public WebDataServiceConsumer { + public WebDataServiceConsumer, + public NotificationObserver { public: TokenService(); virtual ~TokenService(); @@ -123,6 +127,10 @@ class TokenService : public GaiaAuthConsumer, const bool HasTokenForService(const char* const service) const; const std::string& GetTokenForService(const char* const service) const; + // For tests only. Doesn't save to the WebDB. + void IssueAuthTokenForTest(const std::string& service, + const std::string& auth_token); + // GaiaAuthConsumer implementation. virtual void OnIssueAuthTokenSuccess(const std::string& service, const std::string& auth_token); @@ -133,6 +141,11 @@ class TokenService : public GaiaAuthConsumer, virtual void OnWebDataServiceRequestDone(WebDataService::Handle h, const WDTypedResult* result); + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + private: void FireTokenAvailableNotification(const std::string& service, @@ -169,6 +182,8 @@ class TokenService : public GaiaAuthConsumer, // Map from service to token. std::map<std::string, std::string> token_map_; + NotificationRegistrar registrar_; + FRIEND_TEST_ALL_PREFIXES(TokenServiceTest, LoadTokensIntoMemoryBasic); FRIEND_TEST_ALL_PREFIXES(TokenServiceTest, LoadTokensIntoMemoryAdvanced); diff --git a/chrome/browser/net/gaia/token_service_unittest.cc b/chrome/browser/net/gaia/token_service_unittest.cc index 512efff..ce0096b 100644 --- a/chrome/browser/net/gaia/token_service_unittest.cc +++ b/chrome/browser/net/gaia/token_service_unittest.cc @@ -4,127 +4,18 @@ // // This file defines a unit test for the profile's token service. -#include "chrome/browser/net/gaia/token_service.h" -#include "chrome/browser/password_manager/encryptor.h" -#include "chrome/browser/webdata/web_data_service.h" -#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/browser/net/gaia/token_service_unittest.h" + #include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/test_url_fetcher_factory.h" -#include "chrome/test/signaling_task.h" -#include "chrome/test/test_notification_tracker.h" -#include "chrome/test/testing_profile.h" -#include "testing/gtest/include/gtest/gtest.h" - -// TestNotificationTracker doesn't do a deep copy on the notification details. -// We have to in order to read it out, or we have a bad ptr, since the details -// are a reference on the stack. -class TokenAvailableTracker : public TestNotificationTracker { - public: - const TokenService::TokenAvailableDetails& get_last_token_details() { - return details_; - } - - private: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - TestNotificationTracker::Observe(type, source, details); - if (type == NotificationType::TOKEN_AVAILABLE) { - Details<const TokenService::TokenAvailableDetails> full = details; - details_ = *full.ptr(); - } - } - - TokenService::TokenAvailableDetails details_; -}; - -class TokenFailedTracker : public TestNotificationTracker { - public: - const TokenService::TokenRequestFailedDetails& get_last_token_details() { - return details_; - } - - private: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - TestNotificationTracker::Observe(type, source, details); - if (type == NotificationType::TOKEN_REQUEST_FAILED) { - Details<const TokenService::TokenRequestFailedDetails> full = details; - details_ = *full.ptr(); - } - } - TokenService::TokenRequestFailedDetails details_; -}; - -class TokenServiceTest : public testing::Test { +class TokenServiceTest : public TokenServiceTestHarness { public: - TokenServiceTest() - : ui_thread_(ChromeThread::UI, &message_loop_), - db_thread_(ChromeThread::DB) { - } - virtual void SetUp() { -#if defined(OS_MACOSX) - Encryptor::UseMockKeychain(true); -#endif - credentials_.sid = "sid"; - credentials_.lsid = "lsid"; - credentials_.token = "token"; - credentials_.data = "data"; - - ASSERT_TRUE(db_thread_.Start()); - - profile_.reset(new TestingProfile()); - profile_->CreateWebDataService(false); - WaitForDBLoadCompletion(); - - success_tracker_.ListenFor(NotificationType::TOKEN_AVAILABLE, - Source<TokenService>(&service_)); - failure_tracker_.ListenFor(NotificationType::TOKEN_REQUEST_FAILED, - Source<TokenService>(&service_)); - - service_.Initialize("test", profile_.get()); + TokenServiceTestHarness::SetUp(); service_.UpdateCredentials(credentials_); - - URLFetcher::set_factory(NULL); - } - - virtual void TearDown() { - // You have to destroy the profile before the db_thread_ stops. - if (profile_.get()) { - profile_.reset(NULL); - } - - db_thread_.Stop(); - MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); - MessageLoop::current()->Run(); - } - - void WaitForDBLoadCompletion() { - // The WebDB does all work on the DB thread. This will add an event - // to the end of the DB thread, so when we reach this task, all DB - // operations should be complete. - WaitableEvent done(false, false); - ChromeThread::PostTask( - ChromeThread::DB, FROM_HERE, new SignalingTask(&done)); - done.Wait(); - - // Notifications should be returned from the DB thread onto the UI thread. - message_loop_.RunAllPending(); } - - MessageLoopForUI message_loop_; - ChromeThread ui_thread_; // Mostly so DCHECKS pass. - ChromeThread db_thread_; // WDS on here - - TokenService service_; - TokenAvailableTracker success_tracker_; - TokenFailedTracker failure_tracker_; - GaiaAuthConsumer::ClientLoginResult credentials_; - scoped_ptr<TestingProfile> profile_; }; TEST_F(TokenServiceTest, SanityCheck) { @@ -145,8 +36,7 @@ TEST_F(TokenServiceTest, NotificationSuccess) { EXPECT_EQ(1U, success_tracker_.size()); EXPECT_EQ(0U, failure_tracker_.size()); - TokenService::TokenAvailableDetails details = - success_tracker_.get_last_token_details(); + TokenService::TokenAvailableDetails details = success_tracker_.details(); // MSVC doesn't like this comparison as EQ. EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); EXPECT_EQ(details.token(), "token"); @@ -160,8 +50,7 @@ TEST_F(TokenServiceTest, NotificationFailed) { EXPECT_EQ(0U, success_tracker_.size()); EXPECT_EQ(1U, failure_tracker_.size()); - TokenService::TokenRequestFailedDetails details = - failure_tracker_.get_last_token_details(); + TokenService::TokenRequestFailedDetails details = failure_tracker_.details(); // MSVC doesn't like this comparison as EQ. EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); @@ -275,8 +164,7 @@ TEST_F(TokenServiceTest, LoadTokensIntoMemoryBasic) { service_.LoadTokensIntoMemory(db_tokens, &memory_tokens); EXPECT_EQ(1U, success_tracker_.size()); - TokenService::TokenAvailableDetails details = - success_tracker_.get_last_token_details(); + TokenService::TokenAvailableDetails details = success_tracker_.details(); // MSVC doesn't like this comparison as EQ. EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); EXPECT_EQ(details.token(), "token"); diff --git a/chrome/browser/net/gaia/token_service_unittest.h b/chrome/browser/net/gaia/token_service_unittest.h new file mode 100644 index 0000000..ad46138 --- /dev/null +++ b/chrome/browser/net/gaia/token_service_unittest.h @@ -0,0 +1,130 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file defines a unit test harness for the profile's token service. + +#ifndef CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_UNITTEST_H_ +#define CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_UNITTEST_H_ +#pragma once + +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/password_manager/encryptor.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/test/signaling_task.h" +#include "chrome/test/test_notification_tracker.h" +#include "chrome/test/testing_profile.h" +#include "testing/gtest/include/gtest/gtest.h" + +// TestNotificationTracker doesn't do a deep copy on the notification details. +// We have to in order to read it out, or we have a bad ptr, since the details +// are a reference on the stack. +class TokenAvailableTracker : public TestNotificationTracker { + public: + const TokenService::TokenAvailableDetails& details() { + return details_; + } + + private: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + TestNotificationTracker::Observe(type, source, details); + if (type == NotificationType::TOKEN_AVAILABLE) { + Details<const TokenService::TokenAvailableDetails> full = details; + details_ = *full.ptr(); + } + } + + TokenService::TokenAvailableDetails details_; +}; + +class TokenFailedTracker : public TestNotificationTracker { + public: + const TokenService::TokenRequestFailedDetails& details() { + return details_; + } + + private: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + TestNotificationTracker::Observe(type, source, details); + if (type == NotificationType::TOKEN_REQUEST_FAILED) { + Details<const TokenService::TokenRequestFailedDetails> full = details; + details_ = *full.ptr(); + } + } + + TokenService::TokenRequestFailedDetails details_; +}; + +class TokenServiceTestHarness : public testing::Test { + public: + TokenServiceTestHarness() + : ui_thread_(ChromeThread::UI, &message_loop_), + db_thread_(ChromeThread::DB) { + } + + virtual void SetUp() { +#if defined(OS_MACOSX) + Encryptor::UseMockKeychain(true); +#endif + credentials_.sid = "sid"; + credentials_.lsid = "lsid"; + credentials_.token = "token"; + credentials_.data = "data"; + + ASSERT_TRUE(db_thread_.Start()); + + profile_.reset(new TestingProfile()); + profile_->CreateWebDataService(false); + WaitForDBLoadCompletion(); + + success_tracker_.ListenFor(NotificationType::TOKEN_AVAILABLE, + Source<TokenService>(&service_)); + failure_tracker_.ListenFor(NotificationType::TOKEN_REQUEST_FAILED, + Source<TokenService>(&service_)); + + service_.Initialize("test", profile_.get()); + + URLFetcher::set_factory(NULL); + } + + virtual void TearDown() { + // You have to destroy the profile before the db_thread_ stops. + if (profile_.get()) { + profile_.reset(NULL); + } + + db_thread_.Stop(); + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); + MessageLoop::current()->Run(); + } + + void WaitForDBLoadCompletion() { + // The WebDB does all work on the DB thread. This will add an event + // to the end of the DB thread, so when we reach this task, all DB + // operations should be complete. + WaitableEvent done(false, false); + ChromeThread::PostTask( + ChromeThread::DB, FROM_HERE, new SignalingTask(&done)); + done.Wait(); + + // Notifications should be returned from the DB thread onto the UI thread. + message_loop_.RunAllPending(); + } + + MessageLoopForUI message_loop_; + ChromeThread ui_thread_; // Mostly so DCHECKS pass. + ChromeThread db_thread_; // WDS on here + + TokenService service_; + TokenAvailableTracker success_tracker_; + TokenFailedTracker failure_tracker_; + GaiaAuthConsumer::ClientLoginResult credentials_; + scoped_ptr<TestingProfile> profile_; +}; + +#endif // CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_UNITTEST_H_ diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 2b37c26..075cbf1 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -42,6 +42,7 @@ #include "chrome/browser/search_engines/keyword_editor_controller.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/ssl/ssl_manager.h" +#include "chrome/browser/sync/signin_manager.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tabs/pinned_tab_codec.h" #include "chrome/browser/task_manager.h" @@ -139,6 +140,7 @@ void RegisterUserPrefs(PrefService* user_prefs) { #endif BackgroundContentsService::RegisterUserPrefs(user_prefs); CookiePromptModalDialog::RegisterUserPrefs(user_prefs); + SigninManager::RegisterUserPrefs(user_prefs); } } // namespace browser diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 5c3669c..b2d7eba 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -444,6 +444,11 @@ class OffTheRecordProfileImpl : public Profile, return NULL; } + virtual ProfileSyncService* GetProfileSyncService( + const std::string& cros_user) { + return NULL; + } + virtual CloudPrintProxyService* GetCloudPrintProxyService() { return NULL; } diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index d876d8c..68eec93 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -377,6 +377,11 @@ class Profile { // Returns the ProfileSyncService, creating if not yet created. virtual ProfileSyncService* GetProfileSyncService() = 0; + // Returns the ProfileSyncService, creating if not yet created, with + // the specified CrOS username. + virtual ProfileSyncService* GetProfileSyncService( + const std::string& cros_user) = 0; + // Returns the CloudPrintProxyService, creating if not yet created. virtual CloudPrintProxyService* GetCloudPrintProxyService() = 0; diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc index 6a7cb51..ff6a8da 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -479,6 +479,7 @@ ProfileImpl::~ProfileImpl() { // Delete the NTP resource cache so we can unregister pref observers. ntp_resource_cache_.reset(); + // The sync service needs to be deleted before the services it calls. sync_service_.reset(); // Both HistoryService and WebDataService maintain threads for background @@ -1235,10 +1236,16 @@ TokenService* ProfileImpl::GetTokenService() { } ProfileSyncService* ProfileImpl::GetProfileSyncService() { + return GetProfileSyncService(""); +} + +ProfileSyncService* ProfileImpl::GetProfileSyncService( + const std::string& cros_user) { + if (!ProfileSyncService::IsSyncEnabled()) return NULL; if (!sync_service_.get()) - InitSyncService(); + InitSyncService(cros_user); return sync_service_.get(); } @@ -1248,11 +1255,11 @@ CloudPrintProxyService* ProfileImpl::GetCloudPrintProxyService() { return cloud_print_proxy_service_.get(); } -void ProfileImpl::InitSyncService() { +void ProfileImpl::InitSyncService(const std::string& cros_user) { profile_sync_factory_.reset( new ProfileSyncFactoryImpl(this, CommandLine::ForCurrentProcess())); sync_service_.reset( - profile_sync_factory_->CreateProfileSyncService()); + profile_sync_factory_->CreateProfileSyncService(cros_user)); sync_service_->Initialize(); } diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h index d3831cb..79e4438 100644 --- a/chrome/browser/profile_impl.h +++ b/chrome/browser/profile_impl.h @@ -100,8 +100,10 @@ class ProfileImpl : public Profile, virtual FilePath last_selected_directory(); virtual void set_last_selected_directory(const FilePath& path); virtual ProfileSyncService* GetProfileSyncService(); + virtual ProfileSyncService* GetProfileSyncService( + const std::string& cros_user); virtual TokenService* GetTokenService(); - void InitSyncService(); + void InitSyncService(const std::string& cros_user); virtual CloudPrintProxyService* GetCloudPrintProxyService(); void InitCloudPrintProxyService(); virtual ChromeBlobStorageContext* GetBlobStorageContext(); diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h index 7f81ca2..73d59ef 100644 --- a/chrome/browser/sync/abstract_profile_sync_service_test.h +++ b/chrome/browser/sync/abstract_profile_sync_service_test.h @@ -12,6 +12,7 @@ #include "base/scoped_ptr.h" #include "base/task.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/autofill_model_associator.h" #include "chrome/browser/sync/glue/password_model_associator.h" @@ -55,7 +56,7 @@ class ProfileSyncServiceTestHelper { UserShare* user_share = service->backend()->GetUserShareHandle(); DirectoryManager* dir_manager = user_share->dir_manager.get(); - ScopedDirLookup dir(dir_manager, user_share->authenticated_name); + ScopedDirLookup dir(dir_manager, user_share->name); if (!dir.good()) return false; @@ -120,6 +121,7 @@ class AbstractProfileSyncServiceTest : public testing::Test { MessageLoopForUI message_loop_; ChromeThread ui_thread_; ProfileSyncFactoryMock factory_; + TokenService token_service_; scoped_ptr<TestProfileSyncService> service_; TestIdFactory ids_; }; diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc index 178fe7f..72e1f6d 100644 --- a/chrome/browser/sync/engine/all_status.cc +++ b/chrome/browser/sync/engine/all_status.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "base/port.h" -#include "chrome/browser/sync/engine/auth_watcher.h" #include "chrome/browser/sync/engine/net/server_connection_manager.h" #include "chrome/browser/sync/engine/syncer.h" #include "chrome/browser/sync/engine/syncer_thread.h" @@ -48,11 +47,6 @@ AllStatus::~AllStatus() { delete channel_; } -void AllStatus::WatchConnectionManager(ServerConnectionManager* conn_mgr) { - conn_mgr_hookup_.reset(NewEventListenerHookup(conn_mgr->channel(), this, - &AllStatus::HandleServerConnectionEvent)); -} - void AllStatus::WatchSyncerThread(SyncerThread* syncer_thread) { syncer_thread_hookup_.reset(syncer_thread == NULL ? NULL : syncer_thread->relay_channel()->AddObserver(this)); @@ -99,10 +93,6 @@ AllStatus::Status AllStatus::CalcSyncing(const SyncerEvent &event) const { return status; } -AllStatus::Status AllStatus::CalcSyncing() const { - return CreateBlankStatus(); -} - int AllStatus::CalcStatusChanges(Status* old_status) { int what_changed = 0; @@ -153,34 +143,6 @@ int AllStatus::CalcStatusChanges(Status* old_status) { return what_changed; } -void AllStatus::HandleAuthWatcherEvent(const AuthWatcherEvent& auth_event) { - ScopedStatusLockWithNotify lock(this); - switch (auth_event.what_happened) { - case AuthWatcherEvent::GAIA_AUTH_FAILED: - case AuthWatcherEvent::SERVICE_AUTH_FAILED: - case AuthWatcherEvent::SERVICE_CONNECTION_FAILED: - case AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START: - status_.authenticated = false; - break; - case AuthWatcherEvent::AUTH_SUCCEEDED: - // If we've already calculated that the server is reachable, since we've - // successfully authenticated, we can be confident that the server is up. - if (status_.server_reachable) - status_.server_up = true; - - if (!status_.authenticated) { - status_.authenticated = true; - status_ = CalcSyncing(); - } else { - lock.set_notify_plan(DONT_NOTIFY); - } - break; - default: - lock.set_notify_plan(DONT_NOTIFY); - break; - } -} - void AllStatus::HandleChannelEvent(const SyncerEvent& event) { ScopedStatusLockWithNotify lock(this); switch (event.what_happened) { @@ -222,6 +184,15 @@ void AllStatus::HandleServerConnectionEvent( ScopedStatusLockWithNotify lock(this); status_.server_up = IsGoodReplyFromServer(event.connection_code); status_.server_reachable = event.server_reachable; + + if (event.connection_code == HttpResponse::SERVER_CONNECTION_OK) { + if (!status_.authenticated) { + status_ = CreateBlankStatus(); + } + status_.authenticated = true; + } else { + status_.authenticated = false; + } } } diff --git a/chrome/browser/sync/engine/all_status.h b/chrome/browser/sync/engine/all_status.h index 772f40d..b843a1a 100644 --- a/chrome/browser/sync/engine/all_status.h +++ b/chrome/browser/sync/engine/all_status.h @@ -89,7 +89,6 @@ class AllStatus : public ChannelEventHandler<SyncerEvent> { AllStatus(); ~AllStatus(); - void WatchConnectionManager(ServerConnectionManager* conn_mgr); void HandleServerConnectionEvent(const ServerConnectionEvent& event); void HandleAuthWatcherEvent(const AuthWatcherEvent& event); @@ -116,7 +115,6 @@ class AllStatus : public ChannelEventHandler<SyncerEvent> { // Examines syncer to calculate syncing and the unsynced count, // and returns a Status with new values. - Status CalcSyncing() const; Status CalcSyncing(const SyncerEvent& event) const; Status CreateBlankStatus() const; @@ -125,7 +123,6 @@ class AllStatus : public ChannelEventHandler<SyncerEvent> { Status status_; Channel* const channel_; - scoped_ptr<EventListenerHookup> conn_mgr_hookup_; scoped_ptr<ChannelHookup<SyncerEvent> > syncer_thread_hookup_; scoped_ptr<EventListenerHookup> diskfull_hookup_; scoped_ptr<EventListenerHookup> talk_mediator_hookup_; diff --git a/chrome/browser/sync/engine/auth_watcher.cc b/chrome/browser/sync/engine/auth_watcher.cc deleted file mode 100644 index b414e18..0000000 --- a/chrome/browser/sync/engine/auth_watcher.cc +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/engine/auth_watcher.h" - -#include "base/file_util.h" -#include "base/string_util.h" -#include "chrome/browser/sync/engine/all_status.h" -#include "chrome/browser/sync/engine/authenticator.h" -#include "chrome/browser/sync/engine/net/server_connection_manager.h" -#include "chrome/browser/sync/syncable/directory_manager.h" -#include "chrome/browser/sync/syncable/syncable.h" -#include "chrome/browser/sync/util/user_settings.h" -#include "chrome/common/deprecated/event_sys-inl.h" -#include "chrome/common/net/gaia/gaia_authenticator.h" - -// How authentication happens: -// -// Kick Off: -// The sync API looks to see if the user's name and -// password are stored. If so, it calls authwatcher.Authenticate() with -// them. Otherwise it fires an error event. -// -// On failed Gaia Auth: -// The AuthWatcher attempts to use saved hashes to authenticate -// locally, and on success opens the share. -// On failure, fires an error event. -// -// On successful Gaia Auth: -// AuthWatcher launches a thread to open the share and to get the -// authentication token from the sync server. - -using std::pair; -using std::string; -using std::vector; - -namespace browser_sync { - -AuthWatcher::AuthWatcher(DirectoryManager* dirman, - ServerConnectionManager* scm, - const string& user_agent, - const string& service_id, - const string& gaia_url, - UserSettings* user_settings, - gaia::GaiaAuthenticator* gaia_auth) - : gaia_(gaia_auth), - dirman_(dirman), - scm_(scm), - status_(NOT_AUTHENTICATED), - user_settings_(user_settings), - auth_backend_thread_("SyncEngine_AuthWatcherThread"), - current_attempt_trigger_(AuthWatcherEvent::USER_INITIATED) { - - if (!auth_backend_thread_.Start()) - NOTREACHED() << "Couldn't start SyncEngine_AuthWatcherThread"; - - gaia_->set_message_loop(message_loop()); - loop_proxy_ = auth_backend_thread_.message_loop_proxy(); - - connmgr_hookup_.reset( - NewEventListenerHookup(scm->channel(), this, - &AuthWatcher::HandleServerConnectionEvent)); - AuthWatcherEvent done = { AuthWatcherEvent::AUTHWATCHER_DESTROYED }; - channel_.reset(new Channel(done)); -} - -void AuthWatcher::PersistCredentials() { - DCHECK_EQ(MessageLoop::current(), message_loop()); - gaia::GaiaAuthenticator::AuthResults results = gaia_->results(); - - // We just successfully signed in again, let's clear out any residual cached - // login data from earlier sessions. - ClearAuthenticationData(); - - user_settings_->StoreEmailForSignin(results.email, results.primary_email); - results.email = results.primary_email; - gaia_->SetUsernamePassword(results.primary_email, results.password); - if (!user_settings_->VerifyAgainstStoredHash(results.email, results.password)) - user_settings_->StoreHashedPassword(results.email, results.password); - - user_settings_->SetAuthTokenForService(results.email, - SYNC_SERVICE_NAME, - gaia_->auth_token()); -} - -// TODO(chron): Full integration test suite needed. http://crbug.com/35429 -void AuthWatcher::RenewAuthToken(const std::string& updated_token) { - message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(this, - &AuthWatcher::DoRenewAuthToken, updated_token)); -} - -void AuthWatcher::DoRenewAuthToken(const std::string& updated_token) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - // TODO(chron): We should probably only store auth token in one place. - if (scm_->auth_token() == updated_token) { - return; // This thread is the only one writing to the SCM's auth token. - } - LOG(INFO) << "Updating auth token:" << updated_token; - scm_->set_auth_token(updated_token); - gaia_->RenewAuthToken(updated_token); // Must be on AuthWatcher thread - user_settings_->SetAuthTokenForService(user_settings_->email(), - SYNC_SERVICE_NAME, - updated_token); - - NotifyAuthChanged(user_settings_->email(), updated_token, true); -} - -void AuthWatcher::AuthenticateWithLsid(const std::string& lsid) { - message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(this, - &AuthWatcher::DoAuthenticateWithLsid, lsid)); -} - -void AuthWatcher::DoAuthenticateWithLsid(const std::string& lsid) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - - AuthWatcherEvent event = { AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START }; - NotifyListeners(&event); - - if (gaia_->AuthenticateWithLsid(lsid)) { - PersistCredentials(); - DoAuthenticateWithToken(gaia_->email(), gaia_->auth_token()); - } else { - ProcessGaiaAuthFailure(); - } -} - -const char kAuthWatcher[] = "AuthWatcher"; - -void AuthWatcher::AuthenticateWithToken(const std::string& gaia_email, - const std::string& auth_token) { - message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(this, - &AuthWatcher::DoAuthenticateWithToken, gaia_email, auth_token)); -} - -void AuthWatcher::DoAuthenticateWithToken(const std::string& gaia_email, - const std::string& auth_token) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - - Authenticator auth(scm_, user_settings_); - Authenticator::AuthenticationResult result = - auth.AuthenticateToken(auth_token); - string email = gaia_email; - if (auth.display_email() && *auth.display_email()) { - email = auth.display_email(); - LOG(INFO) << "Auth returned email " << email << " for gaia email " << - gaia_email; - } - - AuthWatcherEvent event = {AuthWatcherEvent::ILLEGAL_VALUE , 0}; - gaia_->SetUsername(email); - gaia_->SetAuthToken(auth_token); - const bool was_authenticated = NOT_AUTHENTICATED != status_; - switch (result) { - case Authenticator::SUCCESS: - { - status_ = GAIA_AUTHENTICATED; - const std::string& share_name = email; - user_settings_->SwitchUser(email); - scm_->set_auth_token(auth_token); - - if (!was_authenticated) { - LOG(INFO) << "Opening DB for AuthenticateWithToken (" - << share_name << ")"; - dirman_->Open(share_name); - } - NotifyAuthChanged(email, auth_token, false); - return; - } - case Authenticator::BAD_AUTH_TOKEN: - event.what_happened = AuthWatcherEvent::SERVICE_AUTH_FAILED; - break; - case Authenticator::CORRUPT_SERVER_RESPONSE: - case Authenticator::SERVICE_DOWN: - event.what_happened = AuthWatcherEvent::SERVICE_CONNECTION_FAILED; - break; - case Authenticator::USER_NOT_ACTIVATED: - event.what_happened = AuthWatcherEvent::SERVICE_USER_NOT_SIGNED_UP; - break; - default: - LOG(FATAL) << "Illegal return from AuthenticateToken"; - return; - } - // Always fall back to local authentication. - if (was_authenticated || AuthenticateLocally(email)) { - if (AuthWatcherEvent::SERVICE_CONNECTION_FAILED == event.what_happened) - return; - } - DCHECK_NE(event.what_happened, AuthWatcherEvent::ILLEGAL_VALUE); - NotifyListeners(&event); -} - -bool AuthWatcher::AuthenticateLocally(string email) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - user_settings_->GetEmailForSignin(&email); - if (file_util::PathExists(FilePath(dirman_->GetSyncDataDatabasePath()))) { - gaia_->SetUsername(email); - status_ = LOCALLY_AUTHENTICATED; - user_settings_->SwitchUser(email); - LOG(INFO) << "Opening DB for AuthenticateLocally (" << email << ")"; - dirman_->Open(email); - NotifyAuthChanged(email, "", false); - return true; - } else { - return false; - } -} - -bool AuthWatcher::AuthenticateLocally(string email, const string& password) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - user_settings_->GetEmailForSignin(&email); - return user_settings_->VerifyAgainstStoredHash(email, password) - && AuthenticateLocally(email); -} - -void AuthWatcher::ProcessGaiaAuthFailure() { - DCHECK_EQ(MessageLoop::current(), message_loop()); - gaia::GaiaAuthenticator::AuthResults results = gaia_->results(); - if (LOCALLY_AUTHENTICATED != status_ && - AuthenticateLocally(results.email, results.password)) { - // TODO(chron): Do we really want a bogus token? - const string auth_token("bogus"); - user_settings_->SetAuthTokenForService(results.email, - SYNC_SERVICE_NAME, - auth_token); - } - AuthWatcherEvent myevent = { AuthWatcherEvent::GAIA_AUTH_FAILED, &results }; - NotifyListeners(&myevent); -} - -void AuthWatcher::DoAuthenticate(const AuthRequest& request) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - - AuthWatcherEvent event = { AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START }; - NotifyListeners(&event); - - current_attempt_trigger_ = request.trigger; - - // We let the caller be lazy and try using the last captcha token seen by - // the gaia authenticator if they haven't provided a token but have sent - // a challenge response. Of course, if the captcha token is specified, - // we use that one instead. - std::string captcha_token(request.captcha_token); - if (!request.captcha_value.empty() && captcha_token.empty()) - captcha_token = gaia_->captcha_token(); - - if (!request.password.empty()) { - bool authenticated = false; - if (!captcha_token.empty()) { - authenticated = gaia_->Authenticate(request.email, request.password, - captcha_token, - request.captcha_value); - } else { - authenticated = gaia_->Authenticate(request.email, request.password); - } - if (authenticated) { - PersistCredentials(); - DoAuthenticateWithToken(gaia_->email(), gaia_->auth_token()); - } else { - ProcessGaiaAuthFailure(); - } - } else if (!request.auth_token.empty()) { - DoAuthenticateWithToken(request.email, request.auth_token); - } else { - LOG(ERROR) << "Attempt to authenticate with no credentials."; - } -} - -void AuthWatcher::NotifyAuthChanged(const string& email, - const string& auth_token, - bool renewed) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - LOG(INFO) << "NotifyAuthSucceeded"; - AuthWatcherEvent event = { - renewed ? - AuthWatcherEvent::AUTH_RENEWED : - AuthWatcherEvent::AUTH_SUCCEEDED - }; - event.user_email = email; - event.auth_token = auth_token; - - NotifyListeners(&event); -} - -void AuthWatcher::HandleServerConnectionEvent( - const ServerConnectionEvent& event) { - message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(this, - &AuthWatcher::DoHandleServerConnectionEvent, event, - scm_->auth_token())); -} - -void AuthWatcher::DoHandleServerConnectionEvent( - const ServerConnectionEvent& event, - const std::string& auth_token_snapshot) { - DCHECK_EQ(MessageLoop::current(), message_loop()); - if (event.server_reachable && - // If the auth_token at the time of the event differs from the current - // one, we have authenticated since then and don't need to re-try. - (auth_token_snapshot == gaia_->auth_token()) && - (event.connection_code == HttpResponse::SYNC_AUTH_ERROR || - status_ == LOCALLY_AUTHENTICATED)) { - // We're either online or just got reconnected and want to try to - // authenticate. If we've got a saved token this should just work. If not - // the auth failure should trigger UI indications that we're not logged in. - - // METRIC: If we get a SYNC_AUTH_ERROR, our token expired. - gaia::GaiaAuthenticator::AuthResults authresults = gaia_->results(); - AuthRequest request = { authresults.email, authresults.password, - authresults.auth_token, std::string(), - std::string(), - AuthWatcherEvent::EXPIRED_CREDENTIALS }; - DoAuthenticate(request); - } -} - -AuthWatcher::~AuthWatcher() { - auth_backend_thread_.Stop(); - // The gaia authenticator takes a const MessageLoop* because it only uses it - // to ensure all methods are invoked on the given loop. Once our thread has - // stopped, the current message loop will be NULL, and no methods should be - // invoked on |gaia_| after this point. We could set it to NULL, but - // abstaining allows for even more sanity checking that nothing is invoked on - // it from now on. -} - -void AuthWatcher::Authenticate(const string& email, const string& password, - const string& captcha_token, const string& captcha_value) { - LOG(INFO) << "AuthWatcher::Authenticate called"; - - string empty; - AuthRequest request = { FormatAsEmailAddress(email), password, empty, - captcha_token, captcha_value, - AuthWatcherEvent::USER_INITIATED }; - message_loop_proxy()->PostTask(FROM_HERE, NewRunnableMethod(this, - &AuthWatcher::DoAuthenticate, request)); -} - -void AuthWatcher::ClearAuthenticationData() { - scm_->set_auth_token(std::string()); - user_settings_->ClearAllServiceTokens(); -} - -string AuthWatcher::email() const { - return gaia_->email(); -} - -void AuthWatcher::NotifyListeners(AuthWatcherEvent* event) { - event->trigger = current_attempt_trigger_; - channel_->NotifyListeners(*event); -} - -} // namespace browser_sync diff --git a/chrome/browser/sync/engine/auth_watcher.h b/chrome/browser/sync/engine/auth_watcher.h deleted file mode 100644 index da90f95..0000000 --- a/chrome/browser/sync/engine/auth_watcher.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// AuthWatcher watches authentication events and user open and close -// events and accordingly opens and closes shares. - -#ifndef CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_ -#define CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_ -#pragma once - -#include <string> - -#include "base/gtest_prod_util.h" -#include "base/message_loop_proxy.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" -#include "base/thread.h" -#include "chrome/browser/sync/protocol/service_constants.h" -#include "chrome/common/deprecated/event_sys.h" -#include "chrome/common/net/gaia/gaia_authenticator.h" - -namespace syncable { -struct DirectoryManagerEvent; -class DirectoryManager; -} - -namespace browser_sync { - -class AuthWatcher; -class ServerConnectionManager; -class URLFactory; -class UserSettings; -struct ServerConnectionEvent; - -struct AuthWatcherEvent { - enum WhatHappened { - AUTHENTICATION_ATTEMPT_START, - AUTHWATCHER_DESTROYED, - AUTH_RENEWED, // Currently only used in testing. - AUTH_SUCCEEDED, - GAIA_AUTH_FAILED, - SERVICE_USER_NOT_SIGNED_UP, - SERVICE_AUTH_FAILED, - SERVICE_CONNECTION_FAILED, - // Used in a safety check in AuthWatcher::AuthenticateWithToken() - ILLEGAL_VALUE, - }; - WhatHappened what_happened; - const gaia::GaiaAuthenticator::AuthResults* auth_results; - // use AuthWatcherEvent as its own traits type in hookups. - typedef AuthWatcherEvent EventType; - static inline bool IsChannelShutdownEvent(const AuthWatcherEvent& event) { - return event.what_happened == AUTHWATCHER_DESTROYED; - } - - // Used for AUTH_SUCCEEDED/AUTH_RENEWED notification. - std::string user_email; - // May be empty if we're only locally authenticated. - std::string auth_token; - - // How was this auth attempt initiated? - enum AuthenticationTrigger { - USER_INITIATED = 0, // default value. - EXPIRED_CREDENTIALS, - }; - - AuthenticationTrigger trigger; -}; - -// The mother-class of Authentication for the sync backend. Handles both gaia -// and sync service authentication via asynchronous Authenticate* methods, -// raising AuthWatcherEvents on success/failure. The implementation currently -// runs its own backend thread for the actual auth processing, which means -// the AuthWatcherEvents can be raised on a different thread than the one that -// invoked authentication. -class AuthWatcher : public base::RefCountedThreadSafe<AuthWatcher> { - friend class AuthWatcherTest; - FRIEND_TEST_ALL_PREFIXES(AuthWatcherTest, Construction); - public: - // Normal progression is local -> gaia -> token. - enum Status { LOCALLY_AUTHENTICATED, GAIA_AUTHENTICATED, NOT_AUTHENTICATED }; - typedef syncable::DirectoryManagerEvent DirectoryManagerEvent; - typedef syncable::DirectoryManager DirectoryManager; - - AuthWatcher(DirectoryManager* dirman, - ServerConnectionManager* scm, - const std::string& user_agent, - const std::string& service_id, - const std::string& gaia_url, - UserSettings* user_settings, - gaia::GaiaAuthenticator* gaia_auth); - ~AuthWatcher(); - - typedef EventChannel<AuthWatcherEvent, Lock> Channel; - - inline Channel* channel() const { - return channel_.get(); - } - - // The following 3 flavors of authentication routines are asynchronous and can - // be called from any thread. - // If |captcha_value| is specified but |captcha_token| is not, this will - // attempt authentication using the last observed captcha token out of - // convenience in the common case so the token doesn't have to be plumbed - // everywhere. - void Authenticate(const std::string& email, const std::string& password, - const std::string& captcha_token, const std::string& captcha_value); - - void Authenticate(const std::string& email, const std::string& password, - bool persist_creds_to_disk) { - Authenticate(email, password, "", ""); - } - - // Use this to update only the token of the current email address. - void RenewAuthToken(const std::string& updated_token); - - // Use this version when you don't need the gaia authentication step because - // you already have a valid LSID cookie for |gaia_email|. - void AuthenticateWithLsid(const std::string& lsid); - - // Use this version when you don't need the gaia authentication step because - // you already have a valid token for |gaia_email|. - void AuthenticateWithToken(const std::string& gaia_email, - const std::string& auth_token); - - // Joins on the backend thread. The AuthWatcher is useless after this and - // should be destroyed. - void Shutdown() { auth_backend_thread_.Stop(); } - - std::string email() const; - syncable::DirectoryManager* dirman() const { return dirman_; } - ServerConnectionManager* scm() const { return scm_; } - UserSettings* settings() const { return user_settings_; } - Status status() const { return (Status)status_; } - - private: - void ClearAuthenticationData(); - - void NotifyAuthChanged(const std::string& email, - const std::string& auth_token, - bool renewed); - void HandleServerConnectionEvent(const ServerConnectionEvent& event); - - void SaveUserSettings(const std::string& username, - const std::string& auth_token); - - MessageLoop* message_loop() { return auth_backend_thread_.message_loop(); } - - base::MessageLoopProxy* message_loop_proxy() { - return loop_proxy_; - } - - void DoRenewAuthToken(const std::string& updated_token); - - // These two helpers should only be called from the auth function. - // Called when authentication with gaia succeeds, to save credential info. - void PersistCredentials(); - // Called when authentication with gaia fails. - void ProcessGaiaAuthFailure(); - - // Just checks that the user has at least one local share cache. - bool AuthenticateLocally(std::string email); - // Also checks the user's password against stored password hash. - bool AuthenticateLocally(std::string email, const std::string& password); - - // Sets the trigger member of the event and sends the event on channel_. - void NotifyListeners(AuthWatcherEvent* event); - - inline std::string FormatAsEmailAddress(const std::string& email) const { - std::string mail(email); - if (email.find('@') == std::string::npos) { - mail.push_back('@'); - // TODO(chron): Should this be done only at the UI level? - mail.append(DEFAULT_SIGNIN_DOMAIN); - } - return mail; - } - - // A struct to marshal various data across to the auth_backend_thread_ on - // Authenticate() and AuthenticateWithToken calls. - struct AuthRequest { - std::string email; - std::string password; - std::string auth_token; - std::string captcha_token; - std::string captcha_value; - bool persist_creds_to_disk; - AuthWatcherEvent::AuthenticationTrigger trigger; - }; - - // The public interface Authenticate methods are proxies to these, which - // can only be called from |auth_backend_thread_|. - void DoAuthenticate(const AuthRequest& request); - void DoAuthenticateWithLsid(const std::string& lsid); - void DoAuthenticateWithToken(const std::string& email, - const std::string& auth_token); - - // The public HandleServerConnectionEvent method proxies to this method, which - // can only be called on |auth_backend_thread_|. - void DoHandleServerConnectionEvent( - const ServerConnectionEvent& event, - const std::string& auth_token_snapshot); - - scoped_ptr<gaia::GaiaAuthenticator> const gaia_; - syncable::DirectoryManager* const dirman_; - ServerConnectionManager* const scm_; - scoped_ptr<EventListenerHookup> connmgr_hookup_; - Status status_; - UserSettings* const user_settings_; - scoped_ptr<Channel> channel_; - - base::Thread auth_backend_thread_; - scoped_refptr<base::MessageLoopProxy> loop_proxy_; - - AuthWatcherEvent::AuthenticationTrigger current_attempt_trigger_; - DISALLOW_COPY_AND_ASSIGN(AuthWatcher); -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_ENGINE_AUTH_WATCHER_H_ diff --git a/chrome/browser/sync/engine/auth_watcher_unittest.cc b/chrome/browser/sync/engine/auth_watcher_unittest.cc deleted file mode 100644 index cc840c7..0000000 --- a/chrome/browser/sync/engine/auth_watcher_unittest.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_ptr.h" -#include "base/scoped_temp_dir.h" -#include "base/test/test_file_util.h" -#include "base/waitable_event.h" -#include "chrome/browser/password_manager/encryptor.h" -#include "chrome/browser/sync/engine/auth_watcher.h" -#include "chrome/browser/sync/engine/syncer_thread.h" -#include "chrome/browser/sync/util/user_settings.h" -#include "chrome/common/deprecated/event_sys-inl.h" -#include "chrome/common/net/http_return.h" -#include "chrome/common/net/gaia/gaia_authenticator.h" -#include "chrome/test/sync/engine/mock_connection_manager.h" -#include "chrome/test/sync/engine/test_directory_setter_upper.h" -#include "testing/gtest/include/gtest/gtest.h" - -static FilePath::CharType kUserSettingsDB[] = - FILE_PATH_LITERAL("Settings.sqlite3"); -static const char* kTestUserAgent = "useragent"; -static const char* kTestServiceId = "serviceid"; -static const char* kTestGaiaURL = "http://gaia_url"; -static const char* kUserDisplayName = "Mr. Auth Watcher"; -static const char* kUserDisplayEmail = "authwatcherdisplay@gmail.com"; -static const char* kTestEmail = "authwatchertest@gmail.com"; -static const char* kWrongPassword = "wrongpassword"; -static const char* kCorrectPassword = "correctpassword"; -static const char* kValidSID = "validSID"; -static const char* kValidLSID = "validLSID"; -static const char* kInvalidAuthToken = "invalidAuthToken"; -static const char* kValidAuthToken = "validAuthToken"; - -namespace browser_sync { - -class GaiaAuthMockForAuthWatcher : public gaia::GaiaAuthenticator { - public: - GaiaAuthMockForAuthWatcher() : GaiaAuthenticator( - kTestUserAgent, kTestServiceId, kTestGaiaURL), - use_bad_auth_token_(false) {} - virtual ~GaiaAuthMockForAuthWatcher() {} - - virtual int GetBackoffDelaySeconds( - int current_backoff_delay) { - return SyncerThread::GetRecommendedDelaySeconds(current_backoff_delay); - } - - void SendBadAuthTokenForNextRequest() { use_bad_auth_token_ = true; } - - std::string renewed_token() { - return renewed_token_; - } - - protected: - bool PerformGaiaRequest(const AuthParams& params, AuthResults* results) { - if (params.password == kWrongPassword) { - results->auth_error = gaia::BadAuthentication; - return false; - } - if (params.password == kCorrectPassword) { - results->sid = kValidSID; - results->lsid = kValidLSID; - results->auth_token = kValidAuthToken; - } - if (use_bad_auth_token_) { - results->auth_token = kInvalidAuthToken; - use_bad_auth_token_ = false; - } - return true; - } - - void RenewAuthToken(const std::string& auth_token) { - renewed_token_ = auth_token; - } - - private: - // Whether we should send an invalid auth token on the next request. - bool use_bad_auth_token_; - std::string renewed_token_; -}; - -class AuthWatcherTest : public testing::Test { - public: - AuthWatcherTest() : metadb_(kUserDisplayEmail), - consumer_ready(false, false), - event_produced(false, false), - last_event_reason_(AuthWatcherEvent::ILLEGAL_VALUE) {} - virtual void SetUp() { -#if defined(OS_MACOSX) - // Need to mock the Keychain for unit tests on Mac to avoid possible - // blocking UI. |SetAuthTokenForService| uses Encryptor. - Encryptor::UseMockKeychain(true); -#endif - metadb_.SetUp(); - connection_.reset(new MockConnectionManager(metadb_.manager(), - metadb_.name())); - // Mock out data that would normally be sent back from a server. - connection()->SetAuthenticationResponseInfo(kValidAuthToken, - kUserDisplayName, kUserDisplayEmail, "ID"); - user_settings_.reset(new UserSettings()); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath user_settings_path = temp_dir_.path().Append(kUserSettingsDB); - user_settings_->Init(user_settings_path); - gaia_auth_ = new GaiaAuthMockForAuthWatcher(); - auth_watcher_ = new AuthWatcher(metadb_.manager(), connection_.get(), - kTestUserAgent, kTestServiceId, kTestGaiaURL, - user_settings_.get(), gaia_auth_); - authwatcher_hookup_.reset(NewEventListenerHookup(auth_watcher_->channel(), - this, &AuthWatcherTest::HandleAuthWatcherEvent)); - } - - virtual void TearDown() { - metadb_.TearDown(); - auth_watcher_->Shutdown(); - EXPECT_FALSE(auth_watcher()->message_loop()); - } - - void HandleAuthWatcherEvent(const AuthWatcherEvent& event) { - if (event.what_happened == AuthWatcherEvent::AUTHWATCHER_DESTROYED) - return; - consumer_ready.Wait(); // Block progress until the test is ready. - - last_event_reason_ = event.what_happened; - if (event.what_happened == AuthWatcherEvent::AUTH_SUCCEEDED) - user_email_ = event.user_email; - - event_produced.Signal(); - } - - AuthWatcherEvent::WhatHappened ConsumeNextEvent() { - consumer_ready.Signal(); - event_produced.Wait(); - return last_event_reason_; - } - - AuthWatcher* auth_watcher() { return auth_watcher_.get(); } - MockConnectionManager* connection() { return connection_.get(); } - GaiaAuthMockForAuthWatcher* gaia_auth() { return gaia_auth_; } - const std::string& user_email() { return user_email_; } - - private: - // Responsible for creating / deleting a temp dir containing user settings DB. - ScopedTempDir temp_dir_; - - // The event listener hookup registered for HandleAuthWatcherEvent. - scoped_ptr<EventListenerHookup> authwatcher_hookup_; - - // The sync engine pieces necessary to run an AuthWatcher. - TriggeredOpenTestDirectorySetterUpper metadb_; - scoped_ptr<MockConnectionManager> connection_; - scoped_ptr<UserSettings> user_settings_; - GaiaAuthMockForAuthWatcher* gaia_auth_; // Owned by auth_watcher_. - scoped_refptr<AuthWatcher> auth_watcher_; - - // This is used to block the AuthWatcherThread when it raises events until we - // are ready to read the event. It is not a manual-reset event, so it goes - // straight back to non-signaled after one thread (the main thread) is - // signaled (or "consumes" the signaled state). - base::WaitableEvent consumer_ready; - - // This is signaled by the AuthWatcherThread after it sets last_event_reason_ - // and possibly user_email_ for us to read. - base::WaitableEvent event_produced; - - // The 'WhatHappened' value from the last AuthWatcherEvent we handled. - AuthWatcherEvent::WhatHappened last_event_reason_; - - // Set when we receive an AUTH_SUCCEEDED event. - std::string user_email_; - - DISALLOW_COPY_AND_ASSIGN(AuthWatcherTest); -}; - -TEST_F(AuthWatcherTest, Construction) { - EXPECT_TRUE(auth_watcher()->message_loop()); - EXPECT_EQ("SyncEngine_AuthWatcherThread", - auth_watcher()->message_loop()->thread_name()); - EXPECT_TRUE(auth_watcher()->auth_backend_thread_.IsRunning()); - EXPECT_EQ(AuthWatcher::NOT_AUTHENTICATED, auth_watcher()->status()); -} - -TEST_F(AuthWatcherTest, AuthenticateGaiaAuthFailure) { - auth_watcher()->Authenticate(kTestEmail, kWrongPassword, - std::string(), // captcha_token - std::string()); // captcha_value - - EXPECT_EQ(AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START, ConsumeNextEvent()); - EXPECT_EQ(AuthWatcherEvent::GAIA_AUTH_FAILED, ConsumeNextEvent()); -} - -TEST_F(AuthWatcherTest, AuthenticateBadAuthToken) { - gaia_auth()->SendBadAuthTokenForNextRequest(); - auth_watcher()->Authenticate(kTestEmail, kCorrectPassword, std::string(), - std::string()); - EXPECT_EQ(AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START, ConsumeNextEvent()); - EXPECT_EQ(AuthWatcherEvent::SERVICE_AUTH_FAILED, ConsumeNextEvent()); -} - -TEST_F(AuthWatcherTest, AuthenticateSuccess) { - auth_watcher()->Authenticate(kTestEmail, kCorrectPassword, std::string(), - std::string()); - EXPECT_EQ(AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START, ConsumeNextEvent()); - EXPECT_EQ(AuthWatcherEvent::AUTH_SUCCEEDED, ConsumeNextEvent()); - - // The server responds with a different email than what we used in the call - // to Authenticate, and the AuthWatcher should have told us about. - EXPECT_EQ(kUserDisplayEmail, user_email()); -} - -TEST_F(AuthWatcherTest, AuthenticateWithTokenBadAuthToken) { - auth_watcher()->AuthenticateWithToken(kTestEmail, kInvalidAuthToken); - EXPECT_EQ(AuthWatcherEvent::SERVICE_AUTH_FAILED, ConsumeNextEvent()); -} - -TEST_F(AuthWatcherTest, AuthenticateWithTokenSuccess) { - auth_watcher()->AuthenticateWithToken(kTestEmail, kValidAuthToken); - EXPECT_EQ(AuthWatcherEvent::AUTH_SUCCEEDED, ConsumeNextEvent()); - EXPECT_EQ(kUserDisplayEmail, user_email()); -} - -// Just check that the thread task was properly issued. -TEST_F(AuthWatcherTest, RenewAuthToken) { - auth_watcher()->Authenticate(kTestEmail, kCorrectPassword, std::string(), - std::string()); - EXPECT_EQ(AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START, ConsumeNextEvent()); - EXPECT_EQ(AuthWatcherEvent::AUTH_SUCCEEDED, ConsumeNextEvent()); - - auth_watcher()->RenewAuthToken("updated_token"); - EXPECT_EQ(AuthWatcherEvent::AUTH_RENEWED, ConsumeNextEvent()); - EXPECT_EQ(gaia_auth()->renewed_token(), "updated_token"); - EXPECT_EQ(connection()->auth_token(), "updated_token"); -} - -} // namespace browser_sync diff --git a/chrome/browser/sync/engine/authenticator.cc b/chrome/browser/sync/engine/authenticator.cc deleted file mode 100644 index eec7af3..0000000 --- a/chrome/browser/sync/engine/authenticator.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/engine/authenticator.h" - -#include "chrome/browser/sync/engine/net/server_connection_manager.h" -#include "chrome/browser/sync/engine/syncproto.h" -#include "chrome/browser/sync/protocol/sync.pb.h" -#include "chrome/browser/sync/util/user_settings.h" -#include "chrome/common/deprecated/event_sys-inl.h" -#include "chrome/common/net/gaia/gaia_authenticator.h" - -namespace browser_sync { - -using std::string; - -Authenticator::Authenticator(ServerConnectionManager* manager, - UserSettings* settings) - : server_connection_manager_(manager), settings_(settings) { -} - -Authenticator::Authenticator(ServerConnectionManager* manager) - : server_connection_manager_(manager), settings_(NULL) { -} - -Authenticator::AuthenticationResult Authenticator::Authenticate() { - // TODO(sync): Pull and work with saved credentials. - return NO_SAVED_CREDENTIALS; -} - -Authenticator::AuthenticationResult Authenticator::Authenticate( - string username, string password) { - // TODO(sync): need to figure out if this routine is used anywhere other - // than the test code. - gaia::GaiaAuthenticator auth_service("ChromiumBrowser", "chromiumsync", - "https://www.google.com:443/accounts/ClientLogin"); - auth_service.set_message_loop(MessageLoop::current()); - if (!auth_service.Authenticate(username, password)) { - return UNSPECIFIC_ERROR_RETURN; - } - CHECK(!auth_service.auth_token().empty()); - return AuthenticateToken(auth_service.auth_token()); -} - -COMPILE_ASSERT(sync_pb::ClientToServerResponse::ErrorType_MAX == 7, - client_to_server_response_errors_changed); - -Authenticator::AuthenticationResult Authenticator::HandleSuccessfulTokenRequest( - const sync_pb::UserIdentification* user) { - display_email_ = user->has_email() ? user->email() : ""; - display_name_ = user->has_display_name() ? user->display_name() : ""; - obfuscated_id_ = user->has_obfuscated_id() ? user->obfuscated_id() : ""; - return SUCCESS; -} - -Authenticator::AuthenticationResult Authenticator::AuthenticateToken( - string auth_token) { - ClientToServerMessage client_to_server_message; - // Used to be required for all requests. - client_to_server_message.set_share(""); - client_to_server_message.set_message_contents( - ClientToServerMessage::AUTHENTICATE); - - string tx, rx; - client_to_server_message.SerializeToString(&tx); - HttpResponse http_response; - - ServerConnectionManager::PostBufferParams params = - { tx, &rx, &http_response }; - ScopedServerStatusWatcher watch(server_connection_manager_, &http_response); - if (!server_connection_manager_->PostBufferWithAuth(¶ms, auth_token, - &watch)) { - LOG(WARNING) << "Error posting from authenticator:" << http_response; - return SERVICE_DOWN; - } - sync_pb::ClientToServerResponse response; - if (!response.ParseFromString(rx)) - return CORRUPT_SERVER_RESPONSE; - - switch (response.error_code()) { - case sync_pb::ClientToServerResponse::SUCCESS: - if (response.has_authenticate() && response.authenticate().has_user()) - return HandleSuccessfulTokenRequest(&response.authenticate().user()); - // TODO:(sync) make this CORRUPT_SERVER_RESPONSE when all servers are - // returning user identification at login time. - return SUCCESS; - case sync_pb::ClientToServerResponse::USER_NOT_ACTIVATED: - return USER_NOT_ACTIVATED; - case sync_pb::ClientToServerResponse::AUTH_INVALID: - case sync_pb::ClientToServerResponse::AUTH_EXPIRED: - // TODO(tim): This is an egregious layering violation (bug 35060). - http_response.server_status = HttpResponse::SYNC_AUTH_ERROR; - return BAD_AUTH_TOKEN; - // should never happen (no birthday in this request). - case sync_pb::ClientToServerResponse::NOT_MY_BIRTHDAY: - // should never happen (auth isn't throttled). - case sync_pb::ClientToServerResponse::THROTTLED: - // should never happen (only for stores). - case sync_pb::ClientToServerResponse::ACCESS_DENIED: - // should never happen (only sent on get updates / commit) - case sync_pb::ClientToServerResponse::CLEAR_PENDING: - default: - LOG(ERROR) << "Corrupt Server packet received by auth, error code " << - response.error_code(); - return CORRUPT_SERVER_RESPONSE; - } -} - -} // namespace browser_sync diff --git a/chrome/browser/sync/engine/authenticator.h b/chrome/browser/sync/engine/authenticator.h deleted file mode 100644 index 1abca3c..0000000 --- a/chrome/browser/sync/engine/authenticator.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// The authenticator is a cross-platform class that handles authentication for -// the sync client. -// -// Current State: -// The authenticator is currently only used to authenticate tokens using the -// newer protocol buffer request. - -#ifndef CHROME_BROWSER_SYNC_ENGINE_AUTHENTICATOR_H_ -#define CHROME_BROWSER_SYNC_ENGINE_AUTHENTICATOR_H_ -#pragma once - -#include <string> - -#include "base/basictypes.h" -#include "base/port.h" - -namespace sync_pb { -class UserIdentification; -} - -namespace browser_sync { - -class ServerConnectionManager; -class UserSettings; - -class Authenticator { - public: - // Single return enum. - enum AuthenticationResult { - SUCCESS = 0, - // We couldn't log on because we don't have saved credentials. - NO_SAVED_CREDENTIALS, - // We can't reach auth server (i.e. we're offline or server's down). - NOT_CONNECTED, - // Server's up, but we're down. - SERVICE_DOWN, - // We contacted the server, but the response didn't make sense. - CORRUPT_SERVER_RESPONSE, - // Bad username/password. - BAD_CREDENTIALS, - // Credentials are fine, but the user hasn't signed up. - USER_NOT_ACTIVATED, - - // Return values for internal use. - - // We will never return this to the user unless they call AuthenticateToken - // directly. Other auth functions retry and then return - // CORRUPT_SERVER_RESPONSE. - // TODO(sync): Implement retries. - BAD_AUTH_TOKEN, - // We should never return this, it's a placeholder during development. - // TODO(sync): Remove this - UNSPECIFIC_ERROR_RETURN, - }; - - // Constructor. This class will keep the connection authenticated. - // TODO(sync): Make it work as described. - // TODO(sync): Require a UI callback mechanism. - Authenticator(ServerConnectionManager* manager, UserSettings* settings); - - // Constructor for a simple authenticator used for programmatic login from - // test programs. - explicit Authenticator(ServerConnectionManager* manager); - - // This version of Authenticate tries to use saved credentials, if we have - // any. - AuthenticationResult Authenticate(); - - // We save the username and password in memory (if given) so we - // can refresh the long-lived auth token if it expires. - // Also we save a 10-bit hash of the password to allow offline login. - AuthenticationResult Authenticate(std::string username, std::string password); - - // A version of the auth token to authenticate cookie portion of - // authentication. It uses the new proto buffer based call instead of the HTTP - // GET based one we currently use. - // Can return one of SUCCESS, SERVICE_DOWN, CORRUPT_SERVER_RESPONSE, - // USER_NOT_ACTIVATED or BAD_AUTH_TOKEN. See above for the meaning of these - // values. - // TODO(sync): Make this function private when we're done. - AuthenticationResult AuthenticateToken(std::string auth_token); - - const char* display_email() const { return display_email_.c_str(); } - const char* display_name() const { return display_name_.c_str(); } - private: - // Stores the information in the UserIdentification returned from the server. - AuthenticationResult HandleSuccessfulTokenRequest( - const sync_pb::UserIdentification* user); - // The server connection manager that we're looking after. - ServerConnectionManager* server_connection_manager_; - // Returns SUCCESS or the value that should be returned to the user. - std::string display_email_; - std::string display_name_; - std::string obfuscated_id_; - UserSettings* const settings_; - DISALLOW_COPY_AND_ASSIGN(Authenticator); -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_ENGINE_AUTHENTICATOR_H_ diff --git a/chrome/browser/sync/engine/net/server_connection_manager.cc b/chrome/browser/sync/engine/net/server_connection_manager.cc index 4953836..6d35811 100644 --- a/chrome/browser/sync/engine/net/server_connection_manager.cc +++ b/chrome/browser/sync/engine/net/server_connection_manager.cc @@ -135,11 +135,9 @@ ServerConnectionManager::ServerConnectionManager( const string& server, int port, bool use_ssl, - const string& user_agent, - const string& client_id) + const string& user_agent) : sync_server_(server), sync_server_port_(port), - client_id_(client_id), user_agent_(user_agent), use_ssl_(use_ssl), proto_sync_path_(kSyncServerSyncPath), @@ -163,7 +161,6 @@ void ServerConnectionManager::NotifyStatusChanged() { channel_->NotifyListeners(event); } -// Uses currently set auth token. Set by AuthWatcher. bool ServerConnectionManager::PostBufferWithCachedAuth( const PostBufferParams* params, ScopedServerStatusWatcher* watcher) { string path = @@ -171,14 +168,6 @@ bool ServerConnectionManager::PostBufferWithCachedAuth( return PostBufferToPath(params, path, auth_token(), watcher); } -bool ServerConnectionManager::PostBufferWithAuth(const PostBufferParams* params, - const string& auth_token, ScopedServerStatusWatcher* watcher) { - string path = MakeSyncServerPath(proto_sync_path(), - MakeSyncQueryString(client_id_)); - - return PostBufferToPath(params, path, auth_token, watcher); -} - bool ServerConnectionManager::PostBufferToPath(const PostBufferParams* params, const string& path, const string& auth_token, ScopedServerStatusWatcher* watcher) { diff --git a/chrome/browser/sync/engine/net/server_connection_manager.h b/chrome/browser/sync/engine/net/server_connection_manager.h index 602116a..526dc8e 100644 --- a/chrome/browser/sync/engine/net/server_connection_manager.h +++ b/chrome/browser/sync/engine/net/server_connection_manager.h @@ -213,8 +213,7 @@ class ServerConnectionManager { ServerConnectionManager(const std::string& server, int port, bool use_ssl, - const std::string& user_agent, - const std::string& client_id); + const std::string& user_agent); virtual ~ServerConnectionManager(); @@ -225,14 +224,6 @@ class ServerConnectionManager { virtual bool PostBufferWithCachedAuth(const PostBufferParams* params, ScopedServerStatusWatcher* watcher); - // POSTS buffer_in and reads a response into buffer_out. Add a specific auth - // token to http headers. - // - // Returns true if executed successfully. - virtual bool PostBufferWithAuth(const PostBufferParams* params, - const std::string& auth_token, - ScopedServerStatusWatcher* watcher); - // Checks the time on the server. Returns false if the request failed. |time| // is an out parameter that stores the value returned from the server. virtual bool CheckTime(int32* out_time); @@ -289,6 +280,11 @@ class ServerConnectionManager { return NULL; // For testing. }; + void set_client_id(const std::string& client_id) { + DCHECK(client_id_.empty()); + client_id_.assign(client_id); + } + void set_auth_token(const std::string& auth_token) { // TODO(chron): Consider adding a message loop check here. AutoLock lock(auth_token_mutex_); @@ -333,7 +329,7 @@ class ServerConnectionManager { int sync_server_port_; // The unique id of the user's client. - const std::string client_id_; + std::string client_id_; // The user-agent string for HTTP. std::string user_agent_; diff --git a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h index 67424b9..4108f9a 100644 --- a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h +++ b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h @@ -51,10 +51,8 @@ class SyncAPIServerConnectionManager int port, bool use_ssl, const std::string& client_version, - const std::string& client_id, HttpPostProviderFactory* factory) - : ServerConnectionManager(server, port, use_ssl, client_version, - client_id), + : ServerConnectionManager(server, port, use_ssl, client_version), post_provider_factory_(factory) { DCHECK(post_provider_factory_.get()); } diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 00d1c4f..15e0e8d 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -25,7 +25,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/sync/sync_constants.h" #include "chrome/browser/sync/engine/all_status.h" -#include "chrome/browser/sync/engine/auth_watcher.h" #include "chrome/browser/sync/engine/change_reorder_buffer.h" #include "chrome/browser/sync/engine/model_safe_worker.h" #include "chrome/browser/sync/engine/net/server_connection_manager.h" @@ -49,7 +48,6 @@ #include "chrome/browser/sync/syncable/directory_manager.h" #include "chrome/browser/sync/syncable/syncable.h" #include "chrome/browser/sync/util/crypto_helpers.h" -#include "chrome/browser/sync/util/user_settings.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/deprecated/event_sys.h" #include "chrome/common/net/gaia/gaia_authenticator.h" @@ -61,17 +59,15 @@ using browser_sync::AllStatus; using browser_sync::AllStatusEvent; -using browser_sync::AuthWatcher; -using browser_sync::AuthWatcherEvent; using browser_sync::Cryptographer; using browser_sync::KeyParams; using browser_sync::ModelSafeRoutingInfo; using browser_sync::ModelSafeWorker; using browser_sync::ModelSafeWorkerRegistrar; +using browser_sync::ServerConnectionEvent; using browser_sync::Syncer; using browser_sync::SyncerEvent; using browser_sync::SyncerThread; -using browser_sync::UserSettings; using browser_sync::kNigoriTag; using browser_sync::sessions::SyncSessionContext; using notifier::TalkMediator; @@ -910,7 +906,6 @@ class SyncManager::SyncInternal explicit SyncInternal(SyncManager* sync_manager) : core_message_loop_(NULL), observer_(NULL), - auth_problem_(AuthError::NONE), sync_manager_(sync_manager), registrar_(NULL), notification_pending_(false), @@ -929,40 +924,25 @@ class SyncManager::SyncInternal bool Init(const FilePath& database_location, const std::string& sync_server_and_path, int port, - const char* gaia_service_id, - const char* gaia_source, bool use_ssl, HttpPostProviderFactory* post_factory, - HttpPostProviderFactory* auth_post_factory, ModelSafeWorkerRegistrar* model_safe_worker_registrar, - bool attempt_last_user_authentication, - bool invalidate_last_user_auth_token, - bool invalidate_xmpp_auth_token, const char* user_agent, - const std::string& lsid, + const SyncCredentials& credentials, const bool use_chrome_async_socket, const bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, const std::string& restored_key_for_bootstrapping); - // Tell sync engine to submit credentials to GAIA for verification. - // Successful GAIA authentication will kick off the following chain of events: - // 1. Cause sync engine to open the syncer database. - // 2. Trigger the AuthWatcher to create a Syncer for the directory and call - // SyncerThread::SyncDirectory; the SyncerThread will block until (4). - // 3. Tell the ServerConnectionManager to pass the newly received GAIA auth - // token to a sync server to obtain a sync token. - // 4. On receipt of this token, the ServerConnectionManager broadcasts - // a server-reachable event, which will unblock the SyncerThread. - // 5. When StartSyncing is called, the Syncer will begin the sync process, by - // downloading from or uploading to the server. - // - // If authentication fails, an event will be broadcast all the way up to - // the SyncManager::Observer. It may, in turn, decide to try again with new - // credentials. Calling this method again is the appropriate course of action - // to "retry". - void Authenticate(const std::string& username, const std::string& password, - const std::string& captcha); + + // Sign into sync with given credentials. + // We do not verify the tokens given. After this call, the tokens are set + // and the sync DB is open. True if successful, false if something + // went wrong. + bool SignIn(const SyncCredentials& credentials); + + // Update tokens that we're using in Sync. Email must stay the same. + void UpdateCredentials(const SyncCredentials& credentials); // Tell the sync engine to start the syncing process. void StartSyncing(); @@ -987,20 +967,17 @@ class SyncManager::SyncInternal // This listener is called by the syncer channel for all syncer events. virtual void HandleChannelEvent(const SyncerEvent& event); - // We have a direct hookup to the authwatcher to be notified for auth failures - // on startup, to serve our UI needs. - void HandleAuthWatcherEvent(const AuthWatcherEvent& event); + // Listens for notifications from the ServerConnectionManager + void HandleServerConnectionEvent(const ServerConnectionEvent& event); - // Listen here for directory opened events. - void HandleDirectoryManagerEvent( - const syncable::DirectoryManagerEvent& event); + // Open the directory named with username_for_share + bool OpenDirectory(); // Login to the talk mediator with the given credentials. void TalkMediatorLogin( const std::string& email, const std::string& token); // TalkMediator::Delegate implementation. - virtual void OnNotificationStateChange( bool notifications_enabled); @@ -1016,14 +993,13 @@ class SyncManager::SyncInternal } SyncerThread* syncer_thread() { return syncer_thread_.get(); } TalkMediator* talk_mediator() { return talk_mediator_.get(); } - AuthWatcher* auth_watcher() { return auth_watcher_.get(); } void set_observer(SyncManager::Observer* observer) { observer_ = observer; } UserShare* GetUserShare() { return &share_; } // Return the currently active (validated) username for use with syncable // types. const std::string& username_for_share() const { - return share_.authenticated_name; + return share_.name; } // Note about SyncManager::Status implementation: Status is a trimmed @@ -1078,21 +1054,6 @@ class SyncManager::SyncInternal } private: - // Try to authenticate using a LSID cookie. - void AuthenticateWithLsid(const std::string& lsid); - - // Try to authenticate using persisted credentials from a previous successful - // authentication. If no such credentials exist, calls OnAuthError on the - // client to collect credentials. Otherwise, there exist local credentials - // that were once used for a successful auth, so we'll try to re-use these. - // Failure of that attempt will be communicated as normal using OnAuthError. - // Since this entry point will bypass normal GAIA authentication and try to - // authenticate directly with the sync service using a cached token, - // authentication failure will generally occur due to expired credentials, or - // possibly because of a password change. - bool AuthenticateForUser(const std::string& username, - const std::string& auth_token); - // Helper to call OnAuthError when no authentication credentials are // available. void RaiseAuthNeededEvent(); @@ -1171,10 +1132,6 @@ class SyncManager::SyncInternal // constructing any transaction type. UserShare share_; - // A wrapper around a sqlite store used for caching authentication data, - // last user information, current sync-related URLs, and more. - scoped_ptr<UserSettings> user_settings_; - MessageLoop* core_message_loop_; // Observer registered via SetObserver/RemoveObserver. @@ -1195,13 +1152,6 @@ class SyncManager::SyncInternal // sync components. AllStatus allstatus_; - // AuthWatcher kicks off the authentication process and follows it through - // phase 1 (GAIA) to phase 2 (sync engine). As part of this work it determines - // the initial connectivity and causes the server connection event to be - // broadcast, which signals the syncer thread to start syncing. - // It has a heavy duty constructor requiring boilerplate so we heap allocate. - scoped_refptr<AuthWatcher> auth_watcher_; - // Each element of this array is a store of change records produced by // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are // segregated by model type, and are stored here to be processed and @@ -1213,20 +1163,12 @@ class SyncManager::SyncInternal scoped_ptr<browser_sync::ChannelHookup<syncable::DirectoryChangeEvent> > dir_change_hookup_; + // Event listener hookup for the ServerConnectionManager. + scoped_ptr<EventListenerHookup> connection_manager_hookup_; + // The event listener hookup registered for HandleSyncerEvent. scoped_ptr<browser_sync::ChannelHookup<SyncerEvent> > syncer_event_; - // The event listener hookup registered for HandleAuthWatcherEvent. - scoped_ptr<EventListenerHookup> authwatcher_hookup_; - - // The event listener hookup registered for the DirectoryManager (OPENED). - scoped_ptr<EventListenerHookup> directory_manager_hookup_; - - // Our cache of a recent authentication problem. If no authentication problem - // occurred, or if the last problem encountered has been cleared (by a - // subsequent AuthWatcherEvent), this is set to NONE. - AuthError::State auth_problem_; - // The sync dir_manager to which we belong. SyncManager* const sync_manager_; @@ -1261,17 +1203,11 @@ SyncManager::SyncManager() { bool SyncManager::Init(const FilePath& database_location, const char* sync_server_and_path, int sync_server_port, - const char* gaia_service_id, - const char* gaia_source, bool use_ssl, HttpPostProviderFactory* post_factory, - HttpPostProviderFactory* auth_post_factory, ModelSafeWorkerRegistrar* registrar, - bool attempt_last_user_authentication, - bool invalidate_last_user_auth_token, - bool invalidate_xmpp_auth_token, const char* user_agent, - const char* lsid, + const SyncCredentials& credentials, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -1282,29 +1218,22 @@ bool SyncManager::Init(const FilePath& database_location, return data_->Init(database_location, server_string, sync_server_port, - gaia_service_id, - gaia_source, use_ssl, post_factory, - auth_post_factory, registrar, - attempt_last_user_authentication, - invalidate_last_user_auth_token, - invalidate_xmpp_auth_token, user_agent, - lsid, + credentials, use_chrome_async_socket, try_ssltcp_first, notification_method, restored_key_for_bootstrapping); } -void SyncManager::Authenticate(const char* username, const char* password, - const char* captcha) { - data_->Authenticate(std::string(username), std::string(password), - std::string(captcha)); +void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { + data_->UpdateCredentials(credentials); } + bool SyncManager::InitialSyncEndedForAllEnabledTypes() { return data_->InitialSyncEndedForAllEnabledTypes(); } @@ -1329,6 +1258,7 @@ void SyncManager::RequestNudge() { data_->syncer_thread()->NudgeSyncer(0, SyncerThread::kLocal); } +// TODO(chron): Don't need to plumb this so deep. const std::string& SyncManager::GetAuthenticatedUsername() { DCHECK(data_); return data_->username_for_share(); @@ -1338,17 +1268,11 @@ bool SyncManager::SyncInternal::Init( const FilePath& database_location, const std::string& sync_server_and_path, int port, - const char* gaia_service_id, - const char* gaia_source, bool use_ssl, HttpPostProviderFactory* post_factory, - HttpPostProviderFactory* auth_post_factory, ModelSafeWorkerRegistrar* model_safe_worker_registrar, - bool attempt_last_user_authentication, - bool invalidate_last_user_auth_token, - bool invalidate_xmpp_auth_token, const char* user_agent, - const std::string& lsid, + const SyncCredentials& credentials, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -1359,32 +1283,16 @@ bool SyncManager::SyncInternal::Init( core_message_loop_ = MessageLoop::current(); DCHECK(core_message_loop_); notification_method_ = notification_method; - // Set up UserSettings, creating the db if necessary. We need this to - // instantiate a URLFactory to give to the Syncer. - FilePath settings_db_file = - database_location.Append(FilePath(kBookmarkSyncUserSettingsDatabase)); - user_settings_.reset(new UserSettings()); - if (!user_settings_->Init(settings_db_file)) - return false; - registrar_ = model_safe_worker_registrar; - LOG(INFO) << "Initialized sync user settings. Starting DirectoryManager."; - share_.dir_manager.reset(new DirectoryManager(database_location)); - directory_manager_hookup_.reset(NewEventListenerHookup( - share_.dir_manager->channel(), this, - &SyncInternal::HandleDirectoryManagerEvent)); - share_.dir_manager->cryptographer()->Bootstrap( - restored_key_for_bootstrapping); - string client_id = user_settings_->GetClientId(); connection_manager_.reset(new SyncAPIServerConnectionManager( - sync_server_and_path, port, use_ssl, user_agent, client_id, - post_factory)); + sync_server_and_path, port, use_ssl, user_agent, post_factory)); - // Watch various objects for aggregated status. - allstatus_.WatchConnectionManager(connection_manager()); + connection_manager_hookup_.reset( + NewEventListenerHookup(connection_manager()->channel(), this, + &SyncManager::SyncInternal::HandleServerConnectionEvent)); net::NetworkChangeNotifier::AddObserver(this); // TODO(akalin): CheckServerReachable() can block, which may cause jank if we @@ -1404,7 +1312,7 @@ bool SyncManager::SyncInternal::Init( const bool kInitializeSsl = true; const bool kConnectImmediately = false; talk_mediator_.reset(new TalkMediatorImpl(mediator_thread, kInitializeSsl, - kConnectImmediately, invalidate_xmpp_auth_token)); + kConnectImmediately, false)); if (notification_method != browser_sync::NOTIFICATION_LEGACY && notification_method != browser_sync::NOTIFICATION_SERVER) { if (notification_method == browser_sync::NOTIFICATION_TRANSITIONAL) { @@ -1417,30 +1325,11 @@ bool SyncManager::SyncInternal::Init( // Listen to TalkMediator events ourselves talk_mediator_->SetDelegate(this); - std::string gaia_url = gaia::kGaiaUrl; - const char* service_id = gaia_service_id ? - gaia_service_id : SYNC_SERVICE_NAME; - - BridgedGaiaAuthenticator* gaia_auth = new BridgedGaiaAuthenticator( - gaia_source, service_id, gaia_url, auth_post_factory); - - LOG(INFO) << "Sync is bringing up authwatcher and SyncSessionContext."; - - auth_watcher_ = new AuthWatcher(dir_manager(), - connection_manager(), - gaia_source, - service_id, - gaia_url, - user_settings_.get(), - gaia_auth); - - authwatcher_hookup_.reset(NewEventListenerHookup(auth_watcher_->channel(), - this, &SyncInternal::HandleAuthWatcherEvent)); + LOG(INFO) << "Sync is bringing up SyncSessionContext."; // Build a SyncSessionContext and store the worker in it. SyncSessionContext* context = new SyncSessionContext( - connection_manager_.get(), auth_watcher(), - dir_manager(), model_safe_worker_registrar); + connection_manager_.get(), dir_manager(), model_safe_worker_registrar); // The SyncerThread takes ownership of |context|. syncer_thread_ = new SyncerThread(context); @@ -1449,22 +1338,7 @@ bool SyncManager::SyncInternal::Init( // Subscribe to the syncer thread's channel. syncer_event_.reset(syncer_thread()->relay_channel()->AddObserver(this)); - bool attempting_auth = false; - std::string username, auth_token; - if (attempt_last_user_authentication && - auth_watcher()->settings()->GetLastUserAndServiceToken( - SYNC_SERVICE_NAME, &username, &auth_token)) { - if (invalidate_last_user_auth_token) { - auth_token += "bogus"; - } - attempting_auth = AuthenticateForUser(username, auth_token); - } else if (!lsid.empty()) { - attempting_auth = true; - AuthenticateWithLsid(lsid); - } - if (attempt_last_user_authentication && !attempting_auth) - RaiseAuthNeededEvent(); - return true; + return SignIn(credentials); } void SyncManager::SyncInternal::StartSyncing() { @@ -1536,53 +1410,62 @@ void SyncManager::SyncInternal::SendPendingXMPPNotification( } } -void SyncManager::SyncInternal::Authenticate(const std::string& username, - const std::string& password, - const std::string& captcha) { - DCHECK(username_for_share().empty() || username == username_for_share()) - << "Username change from valid username detected"; - if (allstatus_.status().authenticated) - return; - if (password.empty()) { - // TODO(timsteele): Seems like this shouldn't be needed, but auth_watcher - // currently drops blank password attempts on the floor and doesn't update - // state; it only LOGs an error in this case. We want to make sure we set - // our GoogleServiceAuthError state to denote an error. - RaiseAuthNeededEvent(); +bool SyncManager::SyncInternal::OpenDirectory() { + DCHECK(!initialized()) << "Should only happen once"; + + bool share_opened = dir_manager()->Open(username_for_share()); + DCHECK(share_opened); + if (!share_opened) { + if (observer_) { + observer_->OnStopSyncingPermanently(); + } + + LOG(ERROR) << "Could not open share for:" << username_for_share(); + return false; + } + + // Database has to be initialized for the guid to be available. + syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); + if (!lookup.good()) { + NOTREACHED(); + return false; } - auth_watcher()->Authenticate(username, password, std::string(), - captcha); -} -void SyncManager::SyncInternal::AuthenticateWithLsid(const string& lsid) { - DCHECK(!lsid.empty()); - auth_watcher()->AuthenticateWithLsid(lsid); + connection_manager()->set_client_id(lookup->cache_guid()); + syncer_thread()->CreateSyncer(username_for_share()); + MarkAndNotifyInitializationComplete(); + dir_change_hookup_.reset(lookup->AddChangeObserver(this)); + return true; } -bool SyncManager::SyncInternal::AuthenticateForUser( - const std::string& username, const std::string& auth_token) { - share_.authenticated_name = username; +bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { + DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK(share_.name.empty()); + share_.name = credentials.email; - // We optimize by opening the directory before the "fresh" authentication - // attempt completes so that we can immediately begin processing changes. - if (!dir_manager()->Open(username_for_share())) { - if (observer_) - observer_->OnStopSyncingPermanently(); + LOG(INFO) << "Signing in user: " << username_for_share(); + if (!OpenDirectory()) { return false; } - // Load the last-known good auth token into the connection manager and send - // it off to the AuthWatcher for validation. The result of the validation - // will update the connection manager if necessary. - connection_manager()->set_auth_token(auth_token); - auth_watcher()->AuthenticateWithToken(username, auth_token); + UpdateCredentials(credentials); return true; } +void SyncManager::SyncInternal::UpdateCredentials( + const SyncCredentials& credentials) { + DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK(share_.name == credentials.email); + connection_manager()->set_auth_token(credentials.sync_token); + TalkMediatorLogin(credentials.email, credentials.sync_token); + CheckServerReachable(); + sync_manager_->RequestNudge(); +} + void SyncManager::SyncInternal::RaiseAuthNeededEvent() { - auth_problem_ = AuthError::INVALID_GAIA_CREDENTIALS; - if (observer_) - observer_->OnAuthError(AuthError(auth_problem_)); + if (observer_) { + observer_->OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)); + } } void SyncManager::SyncInternal::SetPassphrase( @@ -1642,15 +1525,6 @@ void SyncManager::SyncInternal::Shutdown() { // TODO(akalin): NULL the other member variables defensively, too. scoped_ptr<TalkMediator> talk_mediator(talk_mediator_.release()); - // First reset the AuthWatcher in case an auth attempt is in progress so that - // it terminates gracefully before we shutdown and close other components. - // Otherwise the attempt can complete after we've closed the directory, for - // example, and cause initialization to continue, which is bad. - if (auth_watcher_) { - auth_watcher_->Shutdown(); - authwatcher_hookup_.reset(); - } - if (syncer_thread()) { if (!syncer_thread()->Stop(kThreadExitTimeoutMsec)) { LOG(FATAL) << "Unable to stop the syncer, it won't be happy..."; @@ -1659,15 +1533,6 @@ void SyncManager::SyncInternal::Shutdown() { syncer_thread_ = NULL; } - // TODO(chron): Since the auth_watcher_ is held by the sync session state, - // we release the ref here after the syncer is deallocated. - // In reality the SyncerSessionState's pointer to the - // authwatcher should be ref counted, but for M6 we use this - // lower risk fix so it's deallocated on the original thread. - if (auth_watcher_) { - auth_watcher_ = NULL; - } - // Shutdown the xmpp buzz connection. if (talk_mediator.get()) { LOG(INFO) << "P2P: Mediator logout started."; @@ -1679,7 +1544,7 @@ void SyncManager::SyncInternal::Shutdown() { // Pump any messages the auth watcher, syncer thread, or talk // mediator posted before they shut down. (See HandleSyncerEvent(), - // HandleAuthWatcherEvent(), and HandleTalkMediatorEvent() for the + // and HandleTalkMediatorEvent() for the // events that may be posted.) { CHECK(core_message_loop_); @@ -1691,6 +1556,8 @@ void SyncManager::SyncInternal::Shutdown() { net::NetworkChangeNotifier::RemoveObserver(this); + connection_manager_hookup_.reset(); + if (dir_manager()) { dir_manager()->FinalSaveChangesForAll(); dir_manager()->Close(username_for_share()); @@ -1699,7 +1566,6 @@ void SyncManager::SyncInternal::Shutdown() { // Reset the DirectoryManager and UserSettings so they relinquish sqlite // handles to backing files. share_.dir_manager.reset(); - user_settings_.reset(); // We don't want to process any more events. dir_change_hookup_.reset(); @@ -1712,22 +1578,7 @@ void SyncManager::SyncInternal::OnIPAddressChanged() { // TODO(akalin): CheckServerReachable() can block, which may cause // jank if we try to shut down sync. Fix this. connection_manager()->CheckServerReachable(); -} - -void SyncManager::SyncInternal::HandleDirectoryManagerEvent( - const syncable::DirectoryManagerEvent& event) { - LOG(INFO) << "Sync internal handling a directory manager event"; - if (syncable::DirectoryManagerEvent::OPENED == event.what_happened) { - DCHECK(!initialized()) << "Should only happen once"; - if (username_for_share().empty()) { - share_.authenticated_name = event.dirname; - } - DCHECK(LowerCaseEqualsASCII(username_for_share(), - StringToLowerASCII(event.dirname).c_str())) - << "username_for_share= " << username_for_share() - << ", event.dirname= " << event.dirname; - MarkAndNotifyInitializationComplete(); - } + sync_manager_->RequestNudge(); } // Listen to model changes, filter out ones initiated by the sync API, and @@ -1750,6 +1601,25 @@ void SyncManager::SyncInternal::HandleChannelEvent( } } +void SyncManager::SyncInternal::HandleServerConnectionEvent( + const ServerConnectionEvent& event) { + allstatus_.HandleServerConnectionEvent(event); + if (event.what_happened == ServerConnectionEvent::STATUS_CHANGED) { + if (event.connection_code == + browser_sync::HttpResponse::SERVER_CONNECTION_OK) { + if (observer_) { + observer_->OnAuthError(AuthError(AuthError::None())); + } + } + + if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { + if (observer_) { + observer_->OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)); + } + } + } +} + void SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( const syncable::DirectoryChangeEvent& event) { // This notification happens immediately before a syncable WriteTransaction @@ -1981,105 +1851,11 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) { observer_->OnStopSyncingPermanently(); return; } -} -void SyncManager::SyncInternal::HandleAuthWatcherEvent( - const AuthWatcherEvent& event) { - allstatus_.HandleAuthWatcherEvent(event); - // We don't care about an authentication attempt starting event, and we - // don't want to reset our state to GoogleServiceAuthError::NONE because the - // fact that an _attempt_ is starting doesn't change the fact that we have an - // auth problem. - if (event.what_happened == AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START) + if (event.what_happened == SyncerEvent::UPDATED_TOKEN) { + observer_->OnUpdatedToken(event.updated_token); return; - // We clear our last auth problem cache on new auth watcher events, and only - // set it to indicate a problem state for certain AuthWatcherEvent types. - auth_problem_ = AuthError::NONE; - switch (event.what_happened) { - case AuthWatcherEvent::AUTH_SUCCEEDED: - DCHECK(!event.user_email.empty()); - // We now know the supplied username and password were valid. If this - // wasn't the first sync, authenticated_name should already be assigned. - if (username_for_share().empty()) { - share_.authenticated_name = event.user_email; - } - - DCHECK(LowerCaseEqualsASCII(username_for_share(), - StringToLowerASCII(event.user_email).c_str())) - << "username_for_share= " << username_for_share() - << ", event.user_email= " << event.user_email; - - if (observer_) - observer_->OnAuthError(AuthError::None()); - - // Hook up the DirectoryChangeEvent listener, HandleChangeEvent. - { - syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); - if (!lookup.good()) { - DCHECK(false) << "ScopedDirLookup creation failed; unable to hook " - << "up directory change event listener!"; - return; - } - - // Note that we can end up here multiple times, for example if the - // user had to re-login and we got a second AUTH_SUCCEEDED event. Take - // care not to add ourselves as an observer a second time. - if (!dir_change_hookup_.get()) - dir_change_hookup_.reset(lookup->AddChangeObserver(this)); - } - - if (!event.auth_token.empty()) { - core_message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod( - this, &SyncManager::SyncInternal::TalkMediatorLogin, - event.user_email, event.auth_token)); - } - return; - case AuthWatcherEvent::AUTH_RENEWED: - DCHECK(!event.user_email.empty()); - DCHECK(!event.auth_token.empty()); - core_message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod( - this, &SyncManager::SyncInternal::TalkMediatorLogin, - event.user_email, event.auth_token)); - return; - // Authentication failures translate to GoogleServiceAuthError events. - case AuthWatcherEvent::GAIA_AUTH_FAILED: // Invalid GAIA credentials. - if (event.auth_results->auth_error == gaia::CaptchaRequired) { - auth_problem_ = AuthError::CAPTCHA_REQUIRED; - std::string url_string("https://www.google.com/accounts/"); - url_string += event.auth_results->captcha_url; - GURL captcha(url_string); - observer_->OnAuthError(AuthError::FromCaptchaChallenge( - event.auth_results->captcha_token, captcha, - GURL(event.auth_results->auth_error_url))); - return; - } else if (event.auth_results->auth_error == - gaia::ConnectionUnavailable) { - auth_problem_ = AuthError::CONNECTION_FAILED; - } else { - auth_problem_ = AuthError::INVALID_GAIA_CREDENTIALS; - } - break; - case AuthWatcherEvent::SERVICE_AUTH_FAILED: // Expired GAIA credentials. - auth_problem_ = AuthError::INVALID_GAIA_CREDENTIALS; - break; - case AuthWatcherEvent::SERVICE_USER_NOT_SIGNED_UP: - auth_problem_ = AuthError::USER_NOT_SIGNED_UP; - break; - case AuthWatcherEvent::SERVICE_CONNECTION_FAILED: - auth_problem_ = AuthError::CONNECTION_FAILED; - break; - default: // We don't care about the many other AuthWatcherEvent types. - return; } - - - // Fire notification that the status changed due to an authentication error. - if (observer_) - observer_->OnAuthError(AuthError(auth_problem_)); } void SyncManager::SyncInternal::OnNotificationStateChange( @@ -2121,8 +1897,8 @@ void SyncManager::SyncInternal::TalkMediatorLogin( << "(talk_mediator_ is NULL)"; return; } - // TODO(akalin): Make talk_mediator automatically login on - // auth token change. + LOG(INFO) << "P2P: Trying talk mediator login."; + talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME); talk_mediator_->Login(); } @@ -2184,7 +1960,7 @@ void SyncManager::SetupForTestMode(const std::wstring& test_username) { void SyncManager::SyncInternal::SetupForTestMode( const std::wstring& test_username) { - share_.authenticated_name = WideToUTF8(test_username); + share_.name = WideToUTF8(test_username); // Some tests are targeting only local db operations & integrity, and don't // want syncer thread interference. @@ -2192,18 +1968,8 @@ void SyncManager::SyncInternal::SetupForTestMode( allstatus_.WatchSyncerThread(NULL); syncer_thread_ = NULL; - if (!dir_manager()->Open(username_for_share())) + if (!dir_manager()->Open(username_for_share())) { DCHECK(false) << "Could not open directory when running in test mode"; - - // Hook up the DirectoryChangeEvent listener, HandleChangeEvent. - { - syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); - if (!lookup.good()) { - DCHECK(false) << "ScopedDirLookup creation failed; unable to hook " - << "up directory change event listener!"; - return; - } - dir_change_hookup_.reset(lookup->AddChangeObserver(this)); } } @@ -2213,7 +1979,7 @@ BaseTransaction::BaseTransaction(UserShare* share) : lookup_(NULL) { DCHECK(share && share->dir_manager.get()); lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(), - share->authenticated_name); + share->name); cryptographer_ = share->dir_manager->cryptographer(); if (!(lookup_->good())) DCHECK(false) << "ScopedDirLookup failed on valid DirManager."; diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index e4e8510..16f8bd9 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -107,12 +107,14 @@ struct UserShare { // be shared across multiple threads (unlike Directory). scoped_ptr<syncable::DirectoryManager> dir_manager; - // The username of the sync user. This is empty until we have performed at - // least one successful GAIA authentication with this username, which means - // on first-run it is empty until an AUTH_SUCCEEDED event and on future runs - // it is set as soon as the client instructs us to authenticate for the last - // known valid user (AuthenticateForLastKnownUser()). - std::string authenticated_name; + // The username of the sync user. + std::string name; +}; + +// Contains everything needed to talk to and identify a user account. +struct SyncCredentials { + std::string email; + std::string sync_token; }; // A valid BaseNode will never have an ID of zero. @@ -669,6 +671,9 @@ class SyncManager { // Called when user interaction may be required due to an auth problem. virtual void OnAuthError(const GoogleServiceAuthError& auth_error) = 0; + // Called when a new auth token is provided by the sync server. + virtual void OnUpdatedToken(const std::string& token) = 0; + // Called when user interaction is required to obtain a valid passphrase. virtual void OnPassphraseRequired() = 0; @@ -715,52 +720,22 @@ class SyncManager { // |sync_server_and_path| and |sync_server_port| represent the Chrome sync // server to use, and |use_ssl| specifies whether to communicate securely; // the default is false. - // |gaia_service_id| is the service id used for GAIA authentication. If it's - // null then default will be used. // |post_factory| will be owned internally and used to create // instances of an HttpPostProvider. - // |auth_post_factory| will be owned internally and used to create - // instances of an HttpPostProvider for communicating with GAIA. - // TODO(timsteele): It seems like one factory should suffice, but for now to - // avoid having to deal with threading issues since the auth code and syncer - // code live on separate threads that run simultaneously, we just dedicate - // one to each component. Long term we may want to reconsider the HttpBridge - // API to take all the params in one chunk in a threadsafe manner.. which is - // still suboptimal as there will be high contention between the two threads - // on startup; so maybe what we have now is the best solution- it does mirror - // the CURL implementation as each thread creates their own internet handle. - // Investigate. // |model_safe_worker| ownership is given to the SyncManager. // |user_agent| is a 7-bit ASCII string suitable for use as the User-Agent // HTTP header. Used internally when collecting stats to classify clients. - // As a fallback when no cached auth information is available, try to - // bootstrap authentication using |lsid|, if it isn't empty. - // - // |invalidate_last_user_auth_token| makes it so that any auth token - // read from user settings is invalidated. This is used for testing - // code paths related to authentication failures. - // - // |invalidate_xmpp_auth_token| makes it so that any auth token - // used to log into XMPP is invalidated. This is used for testing - // code paths related to authentication failures for XMPP only. - // // |try_ssltcp_first| indicates that the SSLTCP port (443) is tried before the // the XMPP port (5222) during login. It is used by the sync tests that are // run on the chromium builders because port 5222 is blocked. bool Init(const FilePath& database_location, const char* sync_server_and_path, int sync_server_port, - const char* gaia_service_id, - const char* gaia_source, bool use_ssl, HttpPostProviderFactory* post_factory, - HttpPostProviderFactory* auth_post_factory, browser_sync::ModelSafeWorkerRegistrar* registrar, - bool attempt_last_user_authentication, - bool invalidate_last_user_auth_token, - bool invalidate_xmpp_auth_token, const char* user_agent, - const char* lsid, + const SyncCredentials& credentials, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -776,15 +751,11 @@ class SyncManager { // called. bool InitialSyncEndedForAllEnabledTypes(); - // Submit credentials to GAIA for verification. On success, both |username| - // and the obtained auth token are persisted on disk for future re-use. - // If authentication fails, OnAuthProblem is called on our Observer. - // The Observer may, in turn, decide to try again with new - // credentials. Calling this method again is the appropriate course of action - // to "retry". - // |username|, |password|, and |captcha| are owned by the caller. - void Authenticate(const char* username, const char* password, - const char* captcha); + // Migrate tokens from user settings DB to the token service. + void MigrateTokens(); + + // Update tokens that we're using in Sync. Email must stay the same. + void UpdateCredentials(const SyncCredentials& credentials); // Start the SyncerThread. void StartSyncing(); diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc index a77134b..b83c501 100644 --- a/chrome/browser/sync/engine/syncapi_unittest.cc +++ b/chrome/browser/sync/engine/syncapi_unittest.cc @@ -25,7 +25,7 @@ class SyncApiTest : public testing::Test { virtual void SetUp() { setter_upper_.SetUp(); share_.dir_manager.reset(setter_upper_.manager()); - share_.authenticated_name = setter_upper_.name(); + share_.name = setter_upper_.name(); } virtual void TearDown() { diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc index 17de218..2cc2300 100644 --- a/chrome/browser/sync/engine/syncer_proto_util.cc +++ b/chrome/browser/sync/engine/syncer_proto_util.cc @@ -6,9 +6,9 @@ #include "base/format_macros.h" #include "base/string_util.h" -#include "chrome/browser/sync/engine/auth_watcher.h" #include "chrome/browser/sync/engine/net/server_connection_manager.h" #include "chrome/browser/sync/engine/syncer.h" +#include "chrome/browser/sync/engine/syncer_types.h" #include "chrome/browser/sync/engine/syncer_util.h" #include "chrome/browser/sync/protocol/service_constants.h" #include "chrome/browser/sync/sessions/sync_session.h" @@ -124,7 +124,7 @@ void SyncerProtoUtil::AddRequestBirthday(syncable::Directory* dir, // static bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm, - AuthWatcher* auth_watcher, + sessions::SyncSession* session, const ClientToServerMessage& msg, ClientToServerResponse* response) { @@ -144,12 +144,9 @@ bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm, std::string new_token = http_response.update_client_auth_header; if (!new_token.empty()) { - // We could also do this in the SCM's PostBufferWithAuth. - // But then we could be in the middle of authentication, which seems - // like a bad time to update the token. A consequence of this is that - // we can't reset the cookie in response to auth attempts, but this - // should be OK. - auth_watcher->RenewAuthToken(new_token); + SyncerEvent event(SyncerEvent::UPDATED_TOKEN); + event.updated_token = new_token; + session->context()->syncer_event_channel()->Notify(event); } if (response->ParseFromString(rx)) { @@ -189,7 +186,7 @@ bool SyncerProtoUtil::PostClientToServerMessage( } if (!PostAndProcessHeaders(session->context()->connection_manager(), - session->context()->auth_watcher(), + session, msg, response)) { return false; diff --git a/chrome/browser/sync/engine/syncer_proto_util.h b/chrome/browser/sync/engine/syncer_proto_util.h index 39fcc5c..ca68ec3 100644 --- a/chrome/browser/sync/engine/syncer_proto_util.h +++ b/chrome/browser/sync/engine/syncer_proto_util.h @@ -105,7 +105,7 @@ class SyncerProtoUtil { // Post the message using the scm, and do some processing on the returned // headers. Decode the server response. static bool PostAndProcessHeaders(browser_sync::ServerConnectionManager* scm, - browser_sync::AuthWatcher* authwatcher, + sessions::SyncSession* session, const ClientToServerMessage& msg, sync_pb::ClientToServerResponse* response); diff --git a/chrome/browser/sync/engine/syncer_proto_util_unittest.cc b/chrome/browser/sync/engine/syncer_proto_util_unittest.cc index e6eb68b..57cae0a 100644 --- a/chrome/browser/sync/engine/syncer_proto_util_unittest.cc +++ b/chrome/browser/sync/engine/syncer_proto_util_unittest.cc @@ -182,7 +182,7 @@ TEST_F(SyncerProtoUtilTest, AddRequestBirthday) { class DummyConnectionManager : public browser_sync::ServerConnectionManager { public: DummyConnectionManager() - : ServerConnectionManager("unused", 0, false, "version", "id"), + : ServerConnectionManager("unused", 0, false, "version"), send_error_(false), access_denied_(false) {} diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc index 800ddddb..9f369ed8 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/syncer_thread.cc @@ -17,12 +17,10 @@ #include "base/rand_util.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" -#include "chrome/browser/sync/engine/auth_watcher.h" #include "chrome/browser/sync/engine/model_safe_worker.h" #include "chrome/browser/sync/engine/net/server_connection_manager.h" #include "chrome/browser/sync/engine/syncer.h" #include "chrome/browser/sync/sessions/session_state.h" -#include "chrome/browser/sync/syncable/directory_manager.h" #include "chrome/common/chrome_switches.h" #include "jingle/notifier/listener/notification_constants.h" @@ -75,19 +73,12 @@ SyncerThread::SyncerThread(sessions::SyncSessionContext* context) syncer_long_poll_interval_seconds_(kDefaultLongPollIntervalSeconds), syncer_polling_interval_(kDefaultShortPollIntervalSeconds), syncer_max_interval_(kDefaultMaxPollIntervalMs), - directory_manager_hookup_(NULL), syncer_events_(NULL), session_context_(context), disable_idle_detection_(false) { DCHECK(context); syncer_event_relay_channel_.reset(new SyncerEventChannel()); - if (context->directory_manager()) { - directory_manager_hookup_.reset(NewEventListenerHookup( - context->directory_manager()->channel(), this, - &SyncerThread::HandleDirectoryManagerEvent)); - } - if (context->connection_manager()) WatchConnectionManager(context->connection_manager()); @@ -98,7 +89,6 @@ SyncerThread::~SyncerThread() { syncer_event_relay_channel_->Notify(SyncerEvent( SyncerEvent::SHUTDOWN_USE_WITH_CARE)); syncer_event_relay_channel_.reset(); - directory_manager_hookup_.reset(); syncer_events_.reset(); delete vault_.syncer_; CHECK(!thread_.IsRunning()); @@ -587,22 +577,18 @@ void SyncerThread::HandleChannelEvent(const SyncerEvent& event) { NudgeSyncImpl(event.nudge_delay_milliseconds, kUnknown); } -void SyncerThread::HandleDirectoryManagerEvent( - const syncable::DirectoryManagerEvent& event) { - LOG(INFO) << "Handling a directory manager event"; - if (syncable::DirectoryManagerEvent::OPENED == event.what_happened) { - AutoLock lock(lock_); - LOG(INFO) << "Syncer starting up for: " << event.dirname; - // The underlying database structure is ready, and we should create - // the syncer. - CHECK(vault_.syncer_ == NULL); - session_context_->set_account_name(event.dirname); - vault_.syncer_ = new Syncer(session_context_.get()); - - syncer_events_.reset( - session_context_->syncer_event_channel()->AddObserver(this)); - vault_field_changed_.Broadcast(); - } +void SyncerThread::CreateSyncer(const std::string& dirname) { + AutoLock lock(lock_); + LOG(INFO) << "Creating syncer up for: " << dirname; + // The underlying database structure is ready, and we should create + // the syncer. + CHECK(vault_.syncer_ == NULL); + session_context_->set_account_name(dirname); + vault_.syncer_ = new Syncer(session_context_.get()); + + syncer_events_.reset( + session_context_->syncer_event_channel()->AddObserver(this)); + vault_field_changed_.Broadcast(); } // Sets |*connected| to false if it is currently true but |code| suggests that diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h index 8c71db4..158d759 100644 --- a/chrome/browser/sync/engine/syncer_thread.h +++ b/chrome/browser/sync/engine/syncer_thread.h @@ -29,11 +29,6 @@ class EventListenerHookup; -namespace syncable { -class DirectoryManager; -struct DirectoryManagerEvent; -} - namespace browser_sync { class ModelSafeWorker; @@ -138,6 +133,9 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, virtual SyncerEventChannel* relay_channel(); + // Call this when a directory is opened + void CreateSyncer(const std::string& dirname); + // DDOS avoidance function. The argument and return value is in seconds static int GetRecommendedDelaySeconds(int base_delay_seconds); @@ -230,8 +228,6 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, friend void* RunSyncerThread(void* syncer_thread); void* Run(); - void HandleDirectoryManagerEvent( - const syncable::DirectoryManagerEvent& event); void HandleChannelEvent(const SyncerEvent& event); // SyncSession::Delegate implementation. @@ -323,7 +319,6 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, // this is called. void NudgeSyncImpl(int milliseconds_from_now, NudgeSource source); - scoped_ptr<EventListenerHookup> directory_manager_hookup_; scoped_ptr<ChannelHookup<SyncerEvent> > syncer_events_; #if defined(OS_LINUX) diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc index 16328bd..ded07f0 100644 --- a/chrome/browser/sync/engine/syncer_thread_unittest.cc +++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc @@ -66,7 +66,7 @@ class SyncerThreadWithSyncerTest : public testing::Test, metadb_.name())); worker_ = new ModelSafeWorker(); SyncSessionContext* context = new SyncSessionContext(connection_.get(), - NULL, metadb_.manager(), this); + metadb_.manager(), this); syncer_thread_ = new SyncerThread(context); syncer_event_hookup_.reset( syncer_thread_->relay_channel()->AddObserver(this)); @@ -217,12 +217,12 @@ class SyncShareIntercept }; TEST_F(SyncerThreadTest, Construction) { - SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, NULL); + SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL); scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); } TEST_F(SyncerThreadTest, StartStop) { - SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, NULL); + SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL); scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); EXPECT_TRUE(syncer_thread->Start()); EXPECT_TRUE(syncer_thread->Stop(2000)); @@ -247,7 +247,7 @@ TEST(SyncerThread, GetRecommendedDelay) { } TEST_F(SyncerThreadTest, CalculateSyncWaitTime) { - SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, NULL); + SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL); scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); syncer_thread->DisableIdleDetection(); @@ -307,7 +307,7 @@ TEST_F(SyncerThreadTest, CalculateSyncWaitTime) { TEST_F(SyncerThreadTest, CalculatePollingWaitTime) { // Set up the environment. int user_idle_milliseconds_param = 0; - SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, NULL); + SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL); scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); syncer_thread->DisableIdleDetection(); // Hold the lock to appease asserts in code. @@ -639,8 +639,8 @@ TEST_F(SyncerThreadWithSyncerTest, Polling) { syncer_thread()->SetSyncerShortPollInterval(poll_interval); EXPECT_TRUE(syncer_thread()->Start()); - // Calling Open() should cause the SyncerThread to create a Syncer. metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); TimeDelta two_polls = poll_interval + poll_interval; // We could theoretically return immediately from the wait if the interceptor @@ -673,6 +673,7 @@ TEST_F(SyncerThreadWithSyncerTest, Nudge) { PreventThreadFromPolling(); EXPECT_TRUE(syncer_thread()->Start()); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); const TimeDelta poll_interval = TimeDelta::FromMinutes(5); interceptor.WaitForSyncShare(1, poll_interval + poll_interval); @@ -697,6 +698,7 @@ TEST_F(SyncerThreadWithSyncerTest, Throttling) { EXPECT_TRUE(syncer_thread()->Start()); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); // Wait for some healthy syncing. interceptor.WaitForSyncShare(4, poll_interval + poll_interval); @@ -750,6 +752,7 @@ TEST_F(SyncerThreadWithSyncerTest, StopSyncPermanently) { EXPECT_TRUE(syncer_thread()->Start()); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); connection()->set_store_birthday("NotYourLuckyDay"); @@ -765,6 +768,7 @@ TEST_F(SyncerThreadWithSyncerTest, AuthInvalid) { syncer_thread()->SetSyncerShortPollInterval(poll_interval); EXPECT_TRUE(syncer_thread()->Start()); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); // Wait for some healthy syncing. interceptor.WaitForSyncShare(2, TimeDelta::FromSeconds(10)); @@ -826,6 +830,7 @@ TEST_F(SyncerThreadWithSyncerTest, FLAKY_Pause) { Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))); ASSERT_TRUE(syncer_thread()->Start()); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); // Request a pause. @@ -869,6 +874,7 @@ TEST_F(SyncerThreadWithSyncerTest, StartWhenNotConnected) { connection()->SetServerNotReachable(); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); // Syncer thread will always go through once cycle at the start, // then it will wait for a connection. @@ -923,6 +929,8 @@ TEST_F(SyncerThreadWithSyncerTest, DISABLED_PauseWhenNotConnected) { Field(&SyncerEvent::what_happened, SyncerEvent::WAITING_FOR_CONNECTION))). WillOnce(SignalEvent(&event)); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); + ASSERT_TRUE(syncer_thread()->Start()); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); ASSERT_TRUE(event.TimedWait(max_wait_time_)); @@ -994,6 +1002,7 @@ TEST_F(SyncerThreadWithSyncerTest, PauseResumeWhenNotRunning) { // Pause the thread then start the syncer. ASSERT_TRUE(Pause(&listener)); metadb()->Open(); + syncer_thread()->CreateSyncer(metadb()->name()); ASSERT_TRUE(syncer_thread()->Start()); // Resume and let the syncer cycle. diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h index 19be54e..0805c89 100644 --- a/chrome/browser/sync/engine/syncer_types.h +++ b/chrome/browser/sync/engine/syncer_types.h @@ -78,6 +78,8 @@ struct SyncerEvent { STATUS_CHANGED, + UPDATED_TOKEN, // new token in updated_token + // Take care not to wait in shutdown handlers for the syncer to stop as it // causes a race in the event system. Use SyncerShutdownEvent instead. SHUTDOWN_USE_WITH_CARE, @@ -143,6 +145,9 @@ struct SyncerEvent { // How many milliseconds later should the syncer kick in? For // REQUEST_SYNC_NUDGE only. int nudge_delay_milliseconds; + + // Update-Client-Auth returns a new token for sync use. + std::string updated_token; }; struct SyncerShutdownEvent { diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc index 0d9ab8e..74e5122 100644 --- a/chrome/browser/sync/engine/syncer_unittest.cc +++ b/chrome/browser/sync/engine/syncer_unittest.cc @@ -168,7 +168,7 @@ class SyncerTest : public testing::Test, new MockConnectionManager(syncdb_.manager(), syncdb_.name())); EnableDatatype(syncable::BOOKMARKS); worker_ = new ModelSafeWorker(); - context_.reset(new SyncSessionContext(mock_server_.get(), NULL, + context_.reset(new SyncSessionContext(mock_server_.get(), syncdb_.manager(), this)); context_->set_account_name(syncdb_.name()); ASSERT_FALSE(context_->syncer_event_channel()); diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index b570c4f..c4e0963 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -10,6 +10,7 @@ #include "base/task.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/sync/engine/syncapi.h" @@ -21,24 +22,25 @@ #include "chrome/browser/sync/glue/password_model_worker.h" #include "chrome/browser/sync/sessions/session_state.h" #include "chrome/common/chrome_version_info.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "webkit/glue/webkit_glue.h" static const int kSaveChangesIntervalSeconds = 10; -static const char kGaiaServiceId[] = "chromiumsync"; -static const char kGaiaSourceForChrome[] = "ChromiumBrowser"; static const FilePath::CharType kSyncDataFolderName[] = FILE_PATH_LITERAL("Sync Data"); using browser_sync::DataTypeController; +typedef TokenService::TokenAvailableDetails TokenAvailableDetails; typedef GoogleServiceAuthError AuthError; namespace browser_sync { using sessions::SyncSessionSnapshot; +using sync_api::SyncCredentials; SyncBackendHost::SyncBackendHost( SyncFrontend* frontend, @@ -75,10 +77,8 @@ void SyncBackendHost::Initialize( const GURL& sync_service_url, const syncable::ModelTypeSet& types, URLRequestContextGetter* baseline_context_getter, - const std::string& lsid, + const SyncCredentials& credentials, bool delete_sync_data_folder, - bool invalidate_sync_login, - bool invalidate_sync_xmpp_login, bool use_chrome_async_socket, bool try_ssltcp_first, NotificationMethod notification_method) { @@ -110,13 +110,10 @@ void SyncBackendHost::Initialize( } InitCore(Core::DoInitializeOptions( - sync_service_url, lsid.empty(), + sync_service_url, MakeHttpBridgeFactory(baseline_context_getter), - MakeHttpBridgeFactory(baseline_context_getter), - lsid, + credentials, delete_sync_data_folder, - invalidate_sync_login, - invalidate_sync_xmpp_login, use_chrome_async_socket, try_ssltcp_first, notification_method, @@ -148,12 +145,11 @@ void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) { options)); } -void SyncBackendHost::Authenticate(const std::string& username, - const std::string& password, - const std::string& captcha) { +void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { core_thread_.message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoAuthenticate, - username, password, captcha)); + NewRunnableMethod(core_.get(), + &SyncBackendHost::Core::DoUpdateCredentials, + credentials)); } void SyncBackendHost::StartSyncingWithServer() { @@ -344,6 +340,15 @@ void SyncBackendHost::Core::NotifyPassphraseAccepted( NotificationService::NoDetails()); } +void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + TokenAvailableDetails details(GaiaConstants::kSyncService, token); + NotificationService::current()->Notify( + NotificationType::TOKEN_UPDATED, + NotificationService::AllSources(), + Details<const TokenAvailableDetails>(&details)); +} + SyncBackendHost::UserShareHandle SyncBackendHost::GetUserShareHandle() const { DCHECK(syncapi_initialized_); return core_->syncapi()->GetUserShare(); @@ -432,8 +437,9 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { // Blow away the partial or corrupt sync data folder before doing any more // initialization, if necessary. - if (options.delete_sync_data_folder) + if (options.delete_sync_data_folder) { DeleteSyncDataFolder(); + } // Make sure that the directory exists before initializing the backend. // If it already exists, this will do no harm. @@ -445,17 +451,11 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { success = syncapi_->Init(path_str, (options.service_url.host() + options.service_url.path()).c_str(), options.service_url.EffectiveIntPort(), - kGaiaServiceId, - kGaiaSourceForChrome, options.service_url.SchemeIsSecure(), options.http_bridge_factory, - options.auth_http_bridge_factory, host_, // ModelSafeWorkerRegistrar. - options.attempt_last_user_authentication, - options.invalidate_sync_login, - options.invalidate_sync_xmpp_login, MakeUserAgentForSyncapi().c_str(), - options.lsid.c_str(), + options.credentials, options.use_chrome_async_socket, options.try_ssltcp_first, options.notification_method, @@ -463,11 +463,10 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { DCHECK(success) << "Syncapi initialization failed!"; } -void SyncBackendHost::Core::DoAuthenticate(const std::string& username, - const std::string& password, - const std::string& captcha) { +void SyncBackendHost::Core::DoUpdateCredentials( + const SyncCredentials& credentials) { DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); - syncapi_->Authenticate(username.c_str(), password.c_str(), captcha.c_str()); + syncapi_->UpdateCredentials(credentials); } void SyncBackendHost::Core::DoStartSyncing() { @@ -624,8 +623,7 @@ bool SyncBackendHost::Core::IsCurrentThreadSafeForModel( void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) { - // We could be on SyncEngine_AuthWatcherThread. Post to our core loop so - // we can modify state. + // Post to our core loop so we can modify state. Could be on another thread. host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, auth_error)); @@ -660,6 +658,11 @@ void SyncBackendHost::Core::OnStopSyncingPermanently() { &Core::HandleStopSyncingPermanentlyOnFrontendLoop)); } +void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) { + host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &Core::NotifyUpdatedToken, token)); +} + void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() { if (!host_ || !host_->frontend_) return; diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index 2b8680d..9e0a9f1 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -16,6 +16,7 @@ #include "base/ref_counted.h" #include "base/thread.h" #include "base/timer.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/engine/model_safe_worker.h" @@ -101,17 +102,14 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void Initialize(const GURL& service_url, const syncable::ModelTypeSet& types, URLRequestContextGetter* baseline_context_getter, - const std::string& lsid, + const sync_api::SyncCredentials& credentials, bool delete_sync_data_folder, - bool invalidate_sync_login, - bool invalidate_sync_xmpp_login, bool use_chrome_async_socket, bool try_ssltcp_first, NotificationMethod notification_method); - // Called on |frontend_loop_| to kick off asynchronous authentication. - void Authenticate(const std::string& username, const std::string& password, - const std::string& captcha); + // Called from |frontend_loop| to update SyncCredentials. + void UpdateCredentials(const sync_api::SyncCredentials& credentials); // This starts the SyncerThread running a Syncer object to communicate with // sync servers. Until this is called, no changes will leave or enter this @@ -208,29 +206,22 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { virtual void OnPaused(); virtual void OnResumed(); virtual void OnStopSyncingPermanently(); + virtual void OnUpdatedToken(const std::string& token); struct DoInitializeOptions { DoInitializeOptions( const GURL& service_url, - bool attempt_last_user_authentication, sync_api::HttpPostProviderFactory* http_bridge_factory, - sync_api::HttpPostProviderFactory* auth_http_bridge_factory, - const std::string& lsid, + const sync_api::SyncCredentials& credentials, bool delete_sync_data_folder, - bool invalidate_sync_login, - bool invalidate_sync_xmpp_login, bool use_chrome_async_socket, bool try_ssltcp_first, NotificationMethod notification_method, std::string restored_key_for_bootstrapping) : service_url(service_url), - attempt_last_user_authentication(attempt_last_user_authentication), http_bridge_factory(http_bridge_factory), - auth_http_bridge_factory(auth_http_bridge_factory), - lsid(lsid), + credentials(credentials), delete_sync_data_folder(delete_sync_data_folder), - invalidate_sync_login(invalidate_sync_login), - invalidate_sync_xmpp_login(invalidate_sync_xmpp_login), use_chrome_async_socket(use_chrome_async_socket), try_ssltcp_first(try_ssltcp_first), notification_method(notification_method), @@ -239,7 +230,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { GURL service_url; bool attempt_last_user_authentication; sync_api::HttpPostProviderFactory* http_bridge_factory; - sync_api::HttpPostProviderFactory* auth_http_bridge_factory; + sync_api::SyncCredentials credentials; std::string lsid; bool delete_sync_data_folder; bool invalidate_sync_login; @@ -260,11 +251,9 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // of the syncapi on behalf of SyncBackendHost::Initialize. void DoInitialize(const DoInitializeOptions& options); - // Called on our SyncBackendHost's core_thread_ to perform authentication - // on behalf of SyncBackendHost::Authenticate. - void DoAuthenticate(const std::string& username, - const std::string& password, - const std::string& captcha); + // Called on our SyncBackendHost's core_thread_ to perform credential + // update on behalf of SyncBackendHost::UpdateCredentials + void DoUpdateCredentials(const sync_api::SyncCredentials& credentials); // Called on the SyncBackendHost core_thread_ to tell the syncapi to start // syncing (generally after initialization and authentication). @@ -308,14 +297,17 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // setup to nudge the syncapi into a useable state. void DoInitializeForTest(const std::wstring& test_user, sync_api::HttpPostProviderFactory* factory, - sync_api::HttpPostProviderFactory* auth_factory, bool delete_sync_data_folder, NotificationMethod notification_method) { - DoInitialize(DoInitializeOptions(GURL(), false, factory, auth_factory, - std::string(), delete_sync_data_folder, - false, false, false, false, - notification_method, "")); - syncapi_->SetupForTestMode(test_user); + // Construct dummy credentials for test. + sync_api::SyncCredentials credentials; + credentials.email = WideToUTF8(test_user); + credentials.sync_token = "token"; + DoInitialize(DoInitializeOptions(GURL(), factory, + credentials, + delete_sync_data_folder, false, false, + notification_method, std::string())); + syncapi_->SetupForTestMode(test_user); } #endif @@ -358,6 +350,9 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // Invoked when the passphrase provided by the user has been accepted. void NotifyPassphraseAccepted(const std::string& bootstrap_token); + // Invoked when an updated token is available from the sync server. + void NotifyUpdatedToken(const std::string& token); + // Called from Core::OnSyncCycleCompleted to handle updating frontend // thread components. void HandleSyncCycleCompletedOnFrontendLoop( @@ -372,6 +367,9 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // Return true if a model lives on the current thread. bool IsCurrentThreadSafeForModel(syncable::ModelType model_type); + // True if credentials are ready for sync use. + bool CredentialsAvailable(); + // Our parent SyncBackendHost SyncBackendHost* host_; diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h index 1ca0473..a4a2550 100644 --- a/chrome/browser/sync/profile_sync_factory.h +++ b/chrome/browser/sync/profile_sync_factory.h @@ -6,7 +6,9 @@ #define CHROME_BROWSER_SYNC_PROFILE_SYNC_FACTORY_H__ #pragma once +#include <string> #include <utility> + #include "base/task.h" #include "chrome/browser/sync/glue/change_processor.h" #include "chrome/browser/sync/glue/data_type_controller.h" @@ -48,7 +50,8 @@ class ProfileSyncFactory { // Instantiates and initializes a new ProfileSyncService. Enabled // data types are registered with the service. The return pointer // is owned by the caller. - virtual ProfileSyncService* CreateProfileSyncService() = 0; + virtual ProfileSyncService* CreateProfileSyncService( + const std::string& cros_user) = 0; // Instantiates a new DataTypeManager with a SyncBackendHost and a // list of data type controllers. The return pointer is owned by diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc index a5df306..d6708cd 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -76,9 +76,11 @@ ProfileSyncFactoryImpl::ProfileSyncFactoryImpl(Profile* profile, command_line_(command_line) { } -ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService() { +ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( + const std::string& cros_user) { + ProfileSyncService* pss = new ProfileSyncService( - this, profile_, browser_defaults::kBootstrapSyncAuthentication); + this, profile_, cros_user); // App sync is enabled by default. Register unless explicitly // disabled. diff --git a/chrome/browser/sync/profile_sync_factory_impl.h b/chrome/browser/sync/profile_sync_factory_impl.h index ce535e5..233f3f1 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.h +++ b/chrome/browser/sync/profile_sync_factory_impl.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_SYNC_PROFILE_SYNC_FACTORY_IMPL_H__ #pragma once +#include <string> + #include "base/basictypes.h" #include "chrome/browser/sync/profile_sync_factory.h" @@ -18,7 +20,8 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory { virtual ~ProfileSyncFactoryImpl() {} // ProfileSyncFactory interface. - virtual ProfileSyncService* CreateProfileSyncService(); + virtual ProfileSyncService* CreateProfileSyncService( + const std::string& cros_user); virtual browser_sync::DataTypeManager* CreateDataTypeManager( browser_sync::SyncBackendHost* backend, diff --git a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc index 908ccae..26f47d1 100644 --- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc +++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc @@ -39,7 +39,7 @@ class ProfileSyncFactoryImplTest : public testing::Test { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDefault) { scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -55,7 +55,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDefault) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableAutofill) { command_line_->AppendSwitch(switches::kDisableSyncAutofill); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -71,7 +71,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableAutofill) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableBookmarks) { command_line_->AppendSwitch(switches::kDisableSyncBookmarks); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -87,7 +87,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableBookmarks) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePreferences) { command_line_->AppendSwitch(switches::kDisableSyncPreferences); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -103,7 +103,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePreferences) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableThemes) { command_line_->AppendSwitch(switches::kDisableSyncThemes); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -119,7 +119,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableThemes) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableExtensions) { command_line_->AppendSwitch(switches::kDisableSyncExtensions); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); @@ -135,7 +135,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableExtensions) { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableApps) { command_line_->AppendSwitch(switches::kDisableSyncApps); scoped_ptr<ProfileSyncService> pss; - pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + pss.reset(profile_sync_service_factory_->CreateProfileSyncService("")); DataTypeController::StateMap controller_states; DataTypeController::StateMap* controller_states_ptr = &controller_states; pss->GetDataTypeControllerStates(controller_states_ptr); diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h index 6c14d1e..18cc30c 100644 --- a/chrome/browser/sync/profile_sync_factory_mock.h +++ b/chrome/browser/sync/profile_sync_factory_mock.h @@ -23,8 +23,8 @@ class ProfileSyncFactoryMock : public ProfileSyncFactory { browser_sync::AssociatorInterface* bookmark_model_associator, browser_sync::ChangeProcessor* bookmark_change_processor); - MOCK_METHOD0(CreateProfileSyncService, - ProfileSyncService*(void)); + MOCK_METHOD1(CreateProfileSyncService, + ProfileSyncService*(const std::string&)); MOCK_METHOD2(CreateDataTypeManager, browser_sync::DataTypeManager*( browser_sync::SyncBackendHost*, diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 5732ebf..567631d 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -30,7 +30,10 @@ #include "chrome/browser/sync/glue/session_data_type_controller.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/syncable/directory_manager.h" +#include "chrome/browser/sync/token_migrator.h" +#include "chrome/browser/sync/util/user_settings.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_details.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" @@ -44,6 +47,7 @@ using browser_sync::ChangeProcessor; using browser_sync::DataTypeController; using browser_sync::DataTypeManager; using browser_sync::SyncBackendHost; +using sync_api::SyncCredentials; typedef GoogleServiceAuthError AuthError; @@ -55,20 +59,20 @@ const char* ProfileSyncService::kDevServerUrl = ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, Profile* profile, - bool bootstrap_sync_authentication) + const std::string& cros_user) : last_auth_error_(AuthError::None()), factory_(factory), profile_(profile), - bootstrap_sync_authentication_(bootstrap_sync_authentication), + cros_user_(cros_user), sync_service_url_(kDevServerUrl), backend_initialized_(false), - expecting_first_run_auth_needed_event_(false), is_auth_in_progress_(false), ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)), unrecoverable_error_detected_(false), use_chrome_async_socket_(false), notification_method_(browser_sync::kDefaultNotificationMethod), - ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)), + token_migrator_(NULL) { DCHECK(factory); DCHECK(profile); registrar_.Add(this, @@ -109,10 +113,8 @@ ProfileSyncService::ProfileSyncService() : last_auth_error_(AuthError::None()), factory_(NULL), profile_(NULL), - bootstrap_sync_authentication_(false), sync_service_url_(kSyncServerUrl), backend_initialized_(false), - expecting_first_run_auth_needed_event_(false), is_auth_in_progress_(false), ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)), unrecoverable_error_detected_(false), @@ -126,6 +128,37 @@ ProfileSyncService::~ProfileSyncService() { Shutdown(false); } +bool ProfileSyncService::AreCredentialsAvailable() { + if (IsManaged()) { + return false; + } + + if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { + return false; + } + + // CrOS user is always logged in. Chrome uses signin_ to check logged in. + if (!cros_user_.empty() || !signin_.GetUsername().empty()) { + // TODO(chron): Verify CrOS unit test behavior. + if (profile()->GetTokenService() && + profile()->GetTokenService()->HasTokenForService( + GaiaConstants::kSyncService)) { + return true; + } + } + return false; +} + +void ProfileSyncService::LoadMigratedCredentials(const std::string& username, + const std::string& token) { + signin_.SetUsername(username); + profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username); + profile()->GetTokenService()->OnIssueAuthTokenSuccess( + GaiaConstants::kSyncService, token); + profile()->GetPrefs()->SetBoolean(prefs::kSyncCredentialsMigrated, true); + token_migrator_.reset(); +} + void ProfileSyncService::Initialize() { LOG(INFO) << "Starting ProfileSyncService."; InitSettings(); @@ -141,24 +174,49 @@ void ProfileSyncService::Initialize() { return; } - if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { - DisableForUser(); // Clean up in case of previous crash / setup abort. + RegisterAuthNotifications(); - // Automatically start sync in Chromium OS. - if (bootstrap_sync_authentication_ && - !profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { - // If the LSID is empty, we're in a CrOS UI test that is not testing sync - // behavior, so we don't want the sync service to start. - if (profile()->GetTokenService() && - !profile()->GetTokenService()->HasLsid()) { - LOG(WARNING) << "Skipping CrOS sync startup, no LSID present."; - return; - } - StartUp(); + // In Chrome, we integrate a SigninManager which works with the sync + // setup wizard to kick off the TokenService. CrOS does its own plumbing + // for the TokenService. + if (cros_user_.empty()) { + // Will load tokens from DB and broadcast Token events after. + signin_.Initialize(profile_); + } + + if (!HasSyncSetupCompleted()) { + DisableForUser(); // Clean up in case of previous crash / setup abort. + if (!cros_user_.empty() && AreCredentialsAvailable()) { + StartUp(); // Under ChromeOS, just autostart it anyway if creds are here. } - } else { + } else if (AreCredentialsAvailable()) { + // If we have credentials and sync setup finished, autostart the backend. + // Note that if we haven't finished setting up sync, backend bring up will + // be done by the wizard. StartUp(); + } else { + // Try to migrate the tokens (if that hasn't already succeeded). + if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncCredentialsMigrated)) { + token_migrator_.reset(new TokenMigrator(this, profile_->GetPath())); + token_migrator_->TryMigration(); + } } + +} + +void ProfileSyncService::RegisterAuthNotifications() { + registrar_.Add(this, + NotificationType::TOKEN_AVAILABLE, + NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::TOKEN_LOADING_FINISHED, + NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, + NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::GOOGLE_SIGNIN_FAILED, + NotificationService::AllSources()); } void ProfileSyncService::RegisterDataTypeController( @@ -232,6 +290,7 @@ void ProfileSyncService::RegisterPreferences() { pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0); pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false); pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false); + pref_service->RegisterBooleanPref(prefs::kSyncCredentialsMigrated, false); // If you've never synced before, or if you're using Chrome OS, all datatypes // are on by default. @@ -269,23 +328,29 @@ void ProfileSyncService::ClearPreferences() { pref_service->ScheduleSavePersistentPrefs(); } +SyncCredentials ProfileSyncService::GetCredentials() { + SyncCredentials credentials; + credentials.email = !cros_user_.empty() ? cros_user_ : signin_.GetUsername(); + DCHECK(!credentials.email.empty()); + TokenService* service = profile_->GetTokenService(); + credentials.sync_token = service->GetTokenForService( + GaiaConstants::kSyncService); + return credentials; +} + void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) { if (!backend_.get()) { NOTREACHED(); return; } - // TODO(akalin): Gather all the command-line-controlled switches - // into an Options struct to make passing them down less annoying. + // TODO(chron): Reimplement invalidate XMPP login / Sync login + // command line switches. Perhaps make it a command + // line in the TokenService itself to pass an arbitrary + // token. - bool invalidate_sync_login = false; - bool invalidate_sync_xmpp_login = false; bool try_ssltcp_first = false; #if !defined(NDEBUG) - invalidate_sync_login = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kInvalidateSyncLogin); - invalidate_sync_xmpp_login = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kInvalidateSyncXmppLogin); try_ssltcp_first = CommandLine::ForCurrentProcess()->HasSwitch( switches::kSyncUseSslTcp); #endif @@ -294,15 +359,17 @@ void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) { // If sync setup hasn't finished, we don't want to initialize routing info // for any data types so that we don't download updates for types that the // user chooses not to sync on the first DownloadUpdatesCommand. - if (HasSyncSetupCompleted()) + if (HasSyncSetupCompleted()) { GetPreferredDataTypes(&types); + } + + SyncCredentials credentials = GetCredentials(); + backend_->Initialize(sync_service_url_, types, profile_->GetRequestContext(), - profile_->GetTokenService()->GetLsid(), + credentials, delete_sync_data_folder, - invalidate_sync_login, - invalidate_sync_xmpp_login, use_chrome_async_socket_, try_ssltcp_first, notification_method_); @@ -321,7 +388,9 @@ void ProfileSyncService::StartUp() { return; } - LOG(INFO) << "ProfileSyncSerivce bringing up backend host."; + DCHECK(AreCredentialsAvailable()); + + LOG(INFO) << "ProfileSyncService bringing up backend host."; last_synced_time_ = base::Time::FromInternalValue( profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); @@ -355,31 +424,21 @@ void ProfileSyncService::Shutdown(bool sync_disabled) { // Clear various flags. is_auth_in_progress_ = false; backend_initialized_ = false; - expecting_first_run_auth_needed_event_ = false; last_attempted_user_email_.clear(); } -void ProfileSyncService::EnableForUser(gfx::NativeWindow parent_window) { - if (WizardIsVisible()) { - wizard_.Focus(); - return; - } - expecting_first_run_auth_needed_event_ = true; - DCHECK(!data_type_manager_.get()); - - wizard_.SetParent(parent_window); - StartUp(); - FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); -} - void ProfileSyncService::DisableForUser() { - LOG(INFO) << "Clearing Sync DB."; + LOG(INFO) << "Disabling sync for user."; - // Clear prefs (including SyncSetupHasCompleted) before shutting down so + // Clear prefs (including SyncSetupHasCompleted) before shutting down so // PSS clients don't think we're set up while we're shutting down. ClearPreferences(); Shutdown(true); + if (cros_user_.empty()) { + signin_.SignOut(); + } + FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } @@ -441,29 +500,20 @@ void ProfileSyncService::OnUnrecoverableError( from_here.file_name(), from_here.line_number())); - // Shut all data types down. - if (data_type_manager_.get()) - data_type_manager_->Stop(); - // Tell the wizard so it can inform the user only if it is already open. wizard_.Step(SyncSetupWizard::FATAL_ERROR); FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); - LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."; + LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable." + << message; std::string location; from_here.Write(true, true, &location); LOG(ERROR) << location; - if (SetupInProgress()) { - // We've hit an error in the middle of a startup process- shutdown all the - // backend stuff, and then restart it, so we're in the same state as before. - MessageLoop::current()->PostTask(FROM_HERE, + // Shut all data types down. + MessageLoop::current()->PostTask(FROM_HERE, scoped_runnable_method_factory_.NewRunnableMethod( &ProfileSyncService::Shutdown, true)); - MessageLoop::current()->PostTask(FROM_HERE, - scoped_runnable_method_factory_.NewRunnableMethod( - &ProfileSyncService::StartUp)); - } } void ProfileSyncService::OnBackendInitialized() { @@ -472,11 +522,12 @@ void ProfileSyncService::OnBackendInitialized() { // The very first time the backend initializes is effectively the first time // we can say we successfully "synced". last_synced_time_ will only be null // in this case, because the pref wasn't restored on StartUp. - if (last_synced_time_.is_null()) + if (last_synced_time_.is_null()) { UpdateLastSyncedTime(); + } FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); - if (bootstrap_sync_authentication_) { + if (!cros_user_.empty()) { if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { ShowChooseDataTypes(NULL); } else { @@ -484,8 +535,9 @@ void ProfileSyncService::OnBackendInitialized() { } } - if (HasSyncSetupCompleted()) + if (HasSyncSetupCompleted()) { ConfigureDataTypeManager(); + } } void ProfileSyncService::OnSyncCycleCompleted() { @@ -493,22 +545,16 @@ void ProfileSyncService::OnSyncCycleCompleted() { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } -void ProfileSyncService::OnAuthError() { - last_auth_error_ = backend_->GetAuthError(); +void ProfileSyncService::UpdateAuthErrorState( + const GoogleServiceAuthError& error) { + last_auth_error_ = error; // Protect against the in-your-face dialogs that pop out of nowhere. // Require the user to click somewhere to run the setup wizard in the case // of a steady-state auth failure. - if (WizardIsVisible() || expecting_first_run_auth_needed_event_) { + if (WizardIsVisible()) { wizard_.Step(AuthError::NONE == last_auth_error_.state() ? SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN); - } - - if (expecting_first_run_auth_needed_event_) { - last_auth_error_ = AuthError::None(); - expecting_first_run_auth_needed_event_ = false; - } - - if (!WizardIsVisible()) { + } else { auth_error_time_ == base::TimeTicks::Now(); } @@ -523,6 +569,10 @@ void ProfileSyncService::OnAuthError() { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } +void ProfileSyncService::OnAuthError() { + UpdateAuthErrorState(backend_->GetAuthError()); + } + void ProfileSyncService::OnStopSyncingPermanently() { if (SetupInProgress()) { wizard_.Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR); @@ -533,6 +583,12 @@ void ProfileSyncService::OnStopSyncingPermanently() { } void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { + // TODO(johnnyg): File a bug to make sure this doesn't happen. + if (!cros_user_.empty()) { + LOG(WARNING) << "ShowLoginDialog called on Chrome OS."; + return; + } + if (WizardIsVisible()) { wizard_.Focus(); return; @@ -544,10 +600,10 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { auth_error_time_ = base::TimeTicks(); // Reset auth_error_time_ to null. } - if (last_auth_error_.state() != AuthError::NONE) { - wizard_.SetParent(parent_window); - wizard_.Step(SyncSetupWizard::GAIA_LOGIN); - } + wizard_.SetParent(parent_window); + wizard_.Step(SyncSetupWizard::GAIA_LOGIN); + + FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } void ProfileSyncService::ShowChooseDataTypes(gfx::NativeWindow parent_window) { @@ -623,16 +679,26 @@ string16 ProfileSyncService::GetAuthenticatedUsername() const { void ProfileSyncService::OnUserSubmittedAuth( const std::string& username, const std::string& password, const std::string& captcha) { - if (!backend_.get()) { - NOTREACHED(); - return; - } last_attempted_user_email_ = username; is_auth_in_progress_ = true; FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); auth_start_time_ = base::TimeTicks::Now(); - backend_->Authenticate(username, password, captcha); + + // TODO(chron): Mechanism for ChromeOS auth renewal? + // (maybe just run the dialog anyway?) + // or send it to the CrOS login somehow? + if (!cros_user_.empty()) { + LOG(WARNING) << "No mechanism on ChromeOS yet. See http://crbug.com/50292"; + } + + if (!signin_.GetUsername().empty()) { + signin_.SignOut(); + } + signin_.StartSignIn(username, + password, + last_auth_error_.captcha().token, + captcha); } void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, @@ -649,7 +715,7 @@ void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, } void ProfileSyncService::OnUserCancelledDialog() { - if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { + if (!HasSyncSetupCompleted()) { // A sync dialog was aborted before authentication. // Rollback. expect_sync_configuration_aborted_ = true; @@ -815,10 +881,43 @@ void ProfileSyncService::Observe(NotificationType type, std::string* pref_name = Details<std::string>(details).ptr(); if (*pref_name == prefs::kSyncManaged) { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); - if (*pref_sync_managed_) + if (*pref_sync_managed_) { DisableForUser(); - else if (HasSyncSetupCompleted()) + } else if (HasSyncSetupCompleted() && AreCredentialsAvailable()) { StartUp(); + } + } + break; + } + case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: { + LOG(INFO) << "Signin OK. Waiting on tokens."; + // TODO(chron): UI update? + // TODO(chron): Timeout? + break; + } + case NotificationType::GOOGLE_SIGNIN_FAILED: { + GoogleServiceAuthError error = + *(Details<GoogleServiceAuthError>(details).ptr()); + UpdateAuthErrorState(error); + break; + } + case NotificationType::TOKEN_AVAILABLE: { + if (AreCredentialsAvailable()) { + if (backend_initialized_) { + backend_->UpdateCredentials(GetCredentials()); + } + + StartUp(); + } + break; + } + case NotificationType::TOKEN_LOADING_FINISHED: { + // If not in Chrome OS, and we have a username without tokens, + // the user will need to signin again, so sign out. + if (cros_user_.empty() && + !signin_.GetUsername().empty() && + !AreCredentialsAvailable()) { + DisableForUser(); } break; } diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index c0f023f..efa2cb2 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -15,12 +15,14 @@ #include "base/string16.h" #include "base/time.h" #include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/data_type_controller.h" #include "chrome/browser/sync/glue/data_type_manager.h" #include "chrome/browser/sync/glue/session_model_associator.h" #include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/profile_sync_service_observer.h" +#include "chrome/browser/sync/signin_manager.h" #include "chrome/browser/sync/sync_setup_wizard.h" #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/unrecoverable_error_handler.h" @@ -34,6 +36,7 @@ class NotificationSource; class NotificationType; class Profile; class ProfileSyncFactory; +class TokenMigrator; // ProfileSyncService is the layer between browser subsystems like bookmarks, // and the sync backend. Each subsystem is logically thought of as being @@ -114,13 +117,23 @@ class ProfileSyncService : public browser_sync::SyncFrontend, ProfileSyncService(ProfileSyncFactory* factory_, Profile* profile, - bool bootstrap_sync_authentication); + const std::string& cros_user); virtual ~ProfileSyncService(); // Initializes the object. This should be called every time an object of this // class is constructed. void Initialize(); + void RegisterAuthNotifications(); + + // Return whether all sync tokens are loaded and + // available for the backend to start up. + bool AreCredentialsAvailable(); + + // Loads credentials migrated from the old user settings db. + void LoadMigratedCredentials(const std::string& username, + const std::string& token); + // Registers a data type controller with the sync service. This // makes the data type controller available for use, it does not // enable or activate the synchronization of the data type (see @@ -138,8 +151,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend, void GetDataTypeControllerStates( browser_sync::DataTypeController::StateMap* state_map) const; - // Enables/disables sync for user. - virtual void EnableForUser(gfx::NativeWindow parent_window); + // Disables sync for user. Use ShowLoginDialog to enable. virtual void DisableForUser(); // Whether sync is enabled by user or not. @@ -157,6 +169,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend, const std::string& password, const std::string& captcha); + // Update the last auth error and notify observers of error state. + void UpdateAuthErrorState(const GoogleServiceAuthError& error); + // Called when a user chooses which data types to sync as part of the sync // setup wizard. |sync_everything| represents whether they chose the // "keep everything synced" option; if true, |chosen_types| will be ignored @@ -326,6 +341,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend, void RegisterPreferences(); void ClearPreferences(); + // Return SyncCredentials from the TokenService. + sync_api::SyncCredentials GetCredentials(); + // Test need to override this to create backends that allow setting up // initial conditions, such as populating sync nodes. virtual void CreateBackend(); @@ -334,10 +352,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend, return data_type_controllers_; } - // We keep track of the last auth error observed so we can cover up the first - // "expected" auth failure from observers. - // TODO(timsteele): Same as expecting_first_run_auth_needed_event_. Remove - // this! + // The wizard will try to read the auth state out of the profile sync + // service using this member. Captcha and error state are reflected. GoogleServiceAuthError last_auth_error_; // Our asynchronous backend to communicate with sync components living on @@ -380,11 +396,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // The profile whose data we are synchronizing. Profile* profile_; - // True if the profile sync service should attempt to use an LSID - // cookie for authentication. This is typically set to true in - // ChromiumOS since we want to use the system level authentication - // for sync. - bool bootstrap_sync_authentication_; + // Email for the ChromiumOS user, if we're running under ChromiumOS. + std::string cros_user_; // TODO(ncarter): Put this in a profile, once there is UI for it. // This specifies where to find the sync server. @@ -400,17 +413,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // Whether the SyncBackendHost has been initialized. bool backend_initialized_; - // Set to true when the user first enables sync, and we are waiting for - // syncapi to give us the green light on providing credentials for the first - // time. It is set back to false as soon as we get this message, and is - // false all other times so we don't have to persist this value as it will - // get initialized to false. - // TODO(timsteele): Remove this by way of starting the wizard when enabling - // sync *before* initializing the backend. syncapi will need to change, but - // it means we don't have to wait for the first AuthError; if we ever get - // one, it is actually an error and this bool isn't needed. - bool expecting_first_run_auth_needed_event_; - // Various pieces of UI query this value to determine if they should show // an "Authenticating.." type of message. We are the only central place // all auth attempts funnel through, so it makes sense to provide this. @@ -419,6 +421,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend, SyncSetupWizard wizard_; + // Encapsulates user signin with TokenService. + SigninManager signin_; + // True if an unrecoverable error (e.g. violation of an assumed invariant) // occurred during syncer operation. This value should be checked before // doing any work that might corrupt things further. @@ -454,6 +459,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // desist syncing immediately. bool expect_sync_configuration_aborted_; + scoped_ptr<TokenMigrator> token_migrator_; + DISALLOW_COPY_AND_ASSIGN(ProfileSyncService); }; diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index e970457..d0884cc 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -31,6 +31,7 @@ #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/web_database.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "chrome/test/profile_mock.h" @@ -137,7 +138,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { void StartSyncService(Task* task, bool will_fail_association) { if (!service_.get()) { service_.reset( - new TestProfileSyncService(&factory_, &profile_, false, false, + new TestProfileSyncService(&factory_, &profile_, "test_user", false, task)); AutofillDataTypeController* data_type_controller = new AutofillDataTypeController(&factory_, @@ -164,6 +165,13 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). WillRepeatedly(Return(true)); + // We need tokens to get the tests going + token_service_.IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); + + EXPECT_CALL(profile_, GetTokenService()). + WillRepeatedly(Return(&token_service_)); + service_->set_num_expected_resumes(will_fail_association ? 0 : 1); service_->RegisterDataTypeController(data_type_controller); service_->Initialize(); diff --git a/chrome/browser/sync/profile_sync_service_mock.h b/chrome/browser/sync/profile_sync_service_mock.h index 1b36c63..3142baf 100644 --- a/chrome/browser/sync/profile_sync_service_mock.h +++ b/chrome/browser/sync/profile_sync_service_mock.h @@ -20,7 +20,6 @@ class ProfileSyncServiceMock : public ProfileSyncService { ProfileSyncServiceMock() {} virtual ~ProfileSyncServiceMock() {} - MOCK_METHOD0(EnableForUser, void()); MOCK_METHOD0(DisableForUser, void()); MOCK_METHOD0(OnBackendInitialized, void()); MOCK_METHOD0(OnSyncCycleCompleted, void()); diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc index c989822..ea53bf7 100644 --- a/chrome/browser/sync/profile_sync_service_password_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc @@ -24,6 +24,7 @@ #include "chrome/browser/sync/syncable/directory_manager.h" #include "chrome/browser/sync/syncable/syncable.h" #include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_observer_mock.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" @@ -140,7 +141,7 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { int num_pause_expectations) { if (!service_.get()) { service_.reset(new TestProfileSyncService(&factory_, &profile_, - false, false, root_task)); + "test_user", false, root_task)); service_->set_num_expected_resumes(num_resume_expectations); service_->set_num_expected_pauses(num_pause_expectations); PasswordDataTypeController* data_type_controller = @@ -155,6 +156,13 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). WillOnce(ReturnNewDataTypeManager()); + // We need tokens to get the tests going + token_service_.IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); + + EXPECT_CALL(profile_, GetTokenService()). + WillRepeatedly(Return(&token_service_)); + // Creating model safe workers will request the history service and // password store. I couldn't manage to convince gmock that splitting up // the expectations to match the class responsibilities was a good thing, diff --git a/chrome/browser/sync/profile_sync_service_preference_unittest.cc b/chrome/browser/sync/profile_sync_service_preference_unittest.cc index b706f7d..e1f3299 100644 --- a/chrome/browser/sync/profile_sync_service_preference_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_preference_unittest.cc @@ -19,6 +19,7 @@ #include "chrome/browser/sync/protocol/preference_specifics.pb.h" #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_pref_service.h" @@ -68,7 +69,7 @@ class ProfileSyncServicePreferenceTest return false; service_.reset(new TestProfileSyncService( - &factory_, profile_.get(), false, false, task)); + &factory_, profile_.get(), "test", false, task)); // Register the preference data type. model_associator_ = @@ -87,6 +88,8 @@ class ProfileSyncServicePreferenceTest service_->RegisterDataTypeController( new PreferenceDataTypeController(&factory_, service_.get())); + profile_->GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); service_->Initialize(); MessageLoop::current()->Run(); return true; diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc index e12192b..23e4885 100644 --- a/chrome/browser/sync/profile_sync_service_session_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc @@ -26,6 +26,7 @@ #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/syncable/syncable.h" #include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" @@ -110,7 +111,7 @@ class ProfileSyncServiceSessionTest return false; sync_service_.reset(new TestProfileSyncService( - &factory_, profile(), false, false, task)); + &factory_, profile(), "test user", false, task)); profile()->set_session_service(helper_.service()); // Register the session data type. @@ -126,6 +127,8 @@ class ProfileSyncServiceSessionTest sync_service_->set_num_expected_resumes(will_fail_association ? 0 : 1); sync_service_->RegisterDataTypeController( new SessionDataTypeController(&factory_, sync_service_.get())); + profile()->GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); sync_service_->Initialize(); MessageLoop::current()->Run(); return true; diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc index d38e6c9..e831e36 100644 --- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc @@ -15,6 +15,7 @@ #include "chrome/browser/sync/profile_sync_test_util.h" #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_profile.h" @@ -40,6 +41,7 @@ ACTION_P(InvokeCallback, callback_result) { #define SKIP_MACOSX(test) test #endif +// TODO(chron): Test not using cros_user flag and use signin_ class ProfileSyncServiceStartupTest : public testing::Test { public: ProfileSyncServiceStartupTest() @@ -54,7 +56,7 @@ class ProfileSyncServiceStartupTest : public testing::Test { virtual void SetUp() { service_.reset(new TestProfileSyncService(&factory_, &profile_, - false, true, NULL)); + "test", true, NULL)); service_->AddObserver(&observer_); service_->set_num_expected_resumes(0); service_->set_num_expected_pauses(0); @@ -99,12 +101,17 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) { Mock::VerifyAndClearExpectations(data_type_manager); // Then start things up. - EXPECT_CALL(*data_type_manager, Configure(_)).Times(1); + EXPECT_CALL(*data_type_manager, Configure(_)).Times(2); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); - EXPECT_CALL(observer_, OnStateChanged()).Times(4); - service_->EnableForUser(NULL); + EXPECT_CALL(observer_, OnStateChanged()).Times(5); + + // Create some tokens in the token service; the service will startup when + // it is notified that tokens are available. + profile_.GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); + syncable::ModelTypeSet set; set.insert(syncable::BOOKMARKS); service_->OnUserChoseDatatypes(false, set); @@ -119,6 +126,9 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartNormal)) { EXPECT_CALL(observer_, OnStateChanged()).Times(3); + // Pre load the tokens + profile_.GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); service_->Initialize(); } @@ -130,6 +140,8 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(ManagedStartup)) { EXPECT_CALL(observer_, OnStateChanged()).Times(1); // Service should not be started by Initialize() since it's managed. + profile_.GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); service_->Initialize(); } @@ -138,6 +150,8 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(SwitchManaged)) { EXPECT_CALL(*data_type_manager, Configure(_)).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(3); + profile_.GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); service_->Initialize(); // The service should stop when switching to managed mode. @@ -164,51 +178,13 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) { WillOnce(DoAll(Notify(NotificationType::SYNC_CONFIGURE_START), NotifyWithResult(NotificationType::SYNC_CONFIGURE_DONE, &result))); - EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::STOPPED)); EXPECT_CALL(observer_, OnStateChanged()).Times(3); + profile_.GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); service_->Initialize(); EXPECT_TRUE(service_->unrecoverable_error_detected()); } - -class ProfileSyncServiceStartupBootstrapTest - : public ProfileSyncServiceStartupTest { - public: - ProfileSyncServiceStartupBootstrapTest() {} - virtual ~ProfileSyncServiceStartupBootstrapTest() {} - - virtual void SetUp() { - service_.reset(new TestProfileSyncService(&factory_, &profile_, - true, true, NULL)); - service_->AddObserver(&observer_); - service_->set_num_expected_resumes(0); - service_->set_num_expected_pauses(0); - service_->set_synchronous_sync_configuration(); - } -}; - -TEST_F(ProfileSyncServiceStartupBootstrapTest, SKIP_MACOSX(StartFirstTime)) { - DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); - EXPECT_CALL(*data_type_manager, Configure(_)).Times(1); - EXPECT_CALL(*data_type_manager, state()). - WillOnce(Return(DataTypeManager::CONFIGURED)); - EXPECT_CALL(*data_type_manager, Stop()).Times(1); - EXPECT_CALL(observer_, OnStateChanged()).Times(4); - - profile_.GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); - - // Pretend the login screen worked. - GaiaAuthConsumer::ClientLoginResult result; - result.sid = "sid"; - result.lsid = "lsid"; - profile_.GetTokenService()->Initialize("test", - &profile_); - profile_.GetTokenService()->UpdateCredentials(result); - - // Will start sync even though setup hasn't been completed (since - // setup is bypassed when bootstrapping is enabled). - service_->Initialize(); -} diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc index 17137ea4..f913433 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -27,6 +27,7 @@ #include "chrome/browser/sync/protocol/typed_url_specifics.pb.h" #include "chrome/browser/sync/syncable/directory_manager.h" #include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_service.h" #include "chrome/test/profile_mock.h" #include "chrome/test/sync/engine/test_id_factory.h" @@ -153,7 +154,7 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { void StartSyncService(Task* task) { if (!service_.get()) { service_.reset( - new TestProfileSyncService(&factory_, &profile_, false, false, + new TestProfileSyncService(&factory_, &profile_, "test", false, task)); TypedUrlDataTypeController* data_type_controller = new TypedUrlDataTypeController(&factory_, @@ -176,7 +177,14 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { EXPECT_CALL(profile_, GetHistoryService(_)). WillRepeatedly(Return(history_service_.get())); + token_service_.IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); + + EXPECT_CALL(profile_, GetTokenService()). + WillRepeatedly(Return(&token_service_)); + service_->RegisterDataTypeController(data_type_controller); + service_->Initialize(); MessageLoop::current()->Run(); } diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc index bb366c7..8ea701a 100644 --- a/chrome/browser/sync/profile_sync_service_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_unittest.cc @@ -15,6 +15,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/sync/engine/syncapi.h" @@ -32,6 +33,7 @@ #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/browser/sync/profile_sync_test_util.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_profile.h" #include "testing/gmock/include/gmock/gmock.h" @@ -246,9 +248,10 @@ class ProfileSyncServiceTest : public testing::Test { } void StartSyncServiceAndSetInitialSyncEnded(bool set_initial_sync_ended) { if (!service_.get()) { + // Set bootstrap to true and it will provide a logged in user for test service_.reset(new TestProfileSyncService(&factory_, profile_.get(), - false, false, NULL)); + "test", false, NULL)); if (!set_initial_sync_ended) service_->dont_set_initial_sync_ended_on_init(); @@ -267,6 +270,9 @@ class ProfileSyncServiceTest : public testing::Test { new browser_sync::BookmarkDataTypeController(&factory_, profile_.get(), service_.get())); + + profile_->GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "token"); service_->Initialize(); MessageLoop::current()->Run(); } @@ -467,15 +473,15 @@ TEST_F(ProfileSyncServiceTest, InitialState) { TEST_F(ProfileSyncServiceTest, AbortedByShutdown) { service_.reset(new TestProfileSyncService(&factory_, profile_.get(), - false, true, NULL)); + "test", true, NULL)); service_->set_num_expected_resumes(0); - EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). - WillOnce(ReturnNewDataTypeManager()); + EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0); EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).Times(0); service_->RegisterDataTypeController( new browser_sync::BookmarkDataTypeController(&factory_, profile_.get(), service_.get())); + service_->Initialize(); service_.reset(); } @@ -1349,9 +1355,9 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) { if (!service_.get()) { service_.reset( new TestProfileSyncService(&factory_, profile_.get(), - false, true, NULL)); - + "test", true, NULL)); service_->dont_set_initial_sync_ended_on_init(); + service_->set_synchronous_sync_configuration(); profile_->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted, false); model_associator_ = new TestBookmarkModelAssociator(service_.get(), @@ -1376,26 +1382,25 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) { ASSERT_FALSE(service_->backend()); ASSERT_FALSE(service_->HasSyncSetupCompleted()); - // This will actually start up the sync service. - service_->EnableForUser(NULL); + // Create some tokens in the token service; the service will startup when + // it is notified that tokens are available. + profile_->GetTokenService()->IssueAuthTokenForTest( + GaiaConstants::kSyncService, "sync_token"); + syncable::ModelTypeSet set; set.insert(syncable::BOOKMARKS); service_->OnUserChoseDatatypes(false, set); - MessageLoop::current()->Run(); + MessageLoop::current()->RunAllPending(); // Stop the service so we can read the new Sync Data files that were created. service_.reset(); // This file should have been deleted when the whole directory was nuked. ASSERT_FALSE(file_util::PathExists(sync_file3)); + ASSERT_FALSE(file_util::PathExists(sync_file1)); - // These two will still exist, but their texts should have changed. - ASSERT_TRUE(file_util::PathExists(sync_file1)); - std::string file1text; - file_util::ReadFileToString(sync_file1, &file1text); - ASSERT_FALSE(file1text.compare(nonsense1) == 0); - + // This will still exist, but the text should have changed. ASSERT_TRUE(file_util::PathExists(sync_file2)); std::string file2text; file_util::ReadFileToString(sync_file2, &file2text); diff --git a/chrome/browser/sync/sessions/sync_session_context.h b/chrome/browser/sync/sessions/sync_session_context.h index 1e37a7c..4c6b63f 100644 --- a/chrome/browser/sync/sessions/sync_session_context.h +++ b/chrome/browser/sync/sessions/sync_session_context.h @@ -31,7 +31,6 @@ class DirectoryManager; namespace browser_sync { -class AuthWatcher; class ConflictResolver; class ModelSafeWorkerRegistrar; class ServerConnectionManager; @@ -43,13 +42,11 @@ class ScopedSessionContextSyncerEventChannel; class SyncSessionContext { public: SyncSessionContext(ServerConnectionManager* connection_manager, - AuthWatcher* auth_watcher, syncable::DirectoryManager* directory_manager, ModelSafeWorkerRegistrar* model_safe_worker_registrar) : resolver_(NULL), syncer_event_channel_(NULL), connection_manager_(connection_manager), - auth_watcher_(auth_watcher), directory_manager_(directory_manager), registrar_(model_safe_worker_registrar), extensions_activity_monitor_(new ExtensionsActivityMonitor()), @@ -68,9 +65,6 @@ class SyncSessionContext { ServerConnectionManager* connection_manager() { return connection_manager_; } - AuthWatcher* auth_watcher() { - return auth_watcher_; - } syncable::DirectoryManager* directory_manager() { return directory_manager_; } @@ -125,7 +119,6 @@ class SyncSessionContext { SyncerEventChannel* syncer_event_channel_; ServerConnectionManager* const connection_manager_; - AuthWatcher* const auth_watcher_; syncable::DirectoryManager* const directory_manager_; // A registrar of workers capable of processing work closures on a thread diff --git a/chrome/browser/sync/sessions/sync_session_unittest.cc b/chrome/browser/sync/sessions/sync_session_unittest.cc index 2bc4594..011f667 100644 --- a/chrome/browser/sync/sessions/sync_session_unittest.cc +++ b/chrome/browser/sync/sessions/sync_session_unittest.cc @@ -27,7 +27,7 @@ class SyncSessionTest : public testing::Test, GetModelSafeRoutingInfo(&routes_); } virtual void SetUp() { - context_.reset(new SyncSessionContext(NULL, NULL, NULL, this)); + context_.reset(new SyncSessionContext(NULL, NULL, this)); session_.reset(new SyncSession(context_.get(), this)); } virtual void TearDown() { @@ -40,7 +40,7 @@ class SyncSessionTest : public testing::Test, } virtual bool IsSyncingCurrentlySilenced() { FailControllerInvocationIfDisabled("IsSyncingCurrentlySilenced"); - return false; + return false; } virtual void OnReceivedLongPollIntervalUpdate( const base::TimeDelta& new_interval) { @@ -51,7 +51,7 @@ class SyncSessionTest : public testing::Test, FailControllerInvocationIfDisabled("OnReceivedShortPollIntervalUpdate"); } virtual void OnShouldStopSyncingPermanently() { - FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently"); + FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently"); } // ModelSafeWorkerRegistrar implementation. @@ -110,7 +110,7 @@ TEST_F(SyncSessionTest, SetWriteTransaction) { TestDirectorySetterUpper db; db.SetUp(); session_.reset(NULL); - context_.reset(new SyncSessionContext(NULL, NULL, db.manager(), this)); + context_.reset(new SyncSessionContext(NULL, db.manager(), this)); session_.reset(new SyncSession(context_.get(), this)); context_->set_account_name(db.name()); syncable::ScopedDirLookup dir(context_->directory_manager(), diff --git a/chrome/browser/sync/signin_manager.cc b/chrome/browser/sync/signin_manager.cc new file mode 100644 index 0000000..6f2a30c --- /dev/null +++ b/chrome/browser/sync/signin_manager.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/signin_manager.h" + +#include "base/string_util.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" + +// static +void SigninManager::RegisterUserPrefs(PrefService* user_prefs) { + user_prefs->RegisterStringPref(prefs::kGoogleServicesUsername, ""); +} + +void SigninManager::Initialize(Profile* profile) { + profile_ = profile; + username_ = profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername); + profile_->GetTokenService()->Initialize( + GaiaConstants::kChromeSource, profile_); + if (!username_.empty()) { + profile_->GetTokenService()->LoadTokensFromDB(); + } +} + +// If a username already exists, the user is logged in. +const std::string& SigninManager::GetUsername() { + return username_; +} + +void SigninManager::SetUsername(const std::string& username) { + username_ = username; +} + +// Users must always sign out before they sign in again. +void SigninManager::StartSignIn(const std::string& username, + const std::string& password, + const std::string& login_token, + const std::string& login_captcha) { + DCHECK(username_.empty()); + // The Sign out should clear the token service credentials. + DCHECK(!profile_->GetTokenService()->AreCredentialsValid()); + + username_.assign(username); + password_.assign(password); + + client_login_.reset(new GaiaAuthenticator2(this, + GaiaConstants::kChromeSource, + profile_->GetRequestContext())); + client_login_->StartClientLogin(username, + password, + "", + login_token, + login_captcha); +} + +void SigninManager::SignOut() { + client_login_.reset(); + username_.clear(); + password_.clear(); + profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username_); + profile_->GetPrefs()->ScheduleSavePersistentPrefs(); + profile_->GetTokenService()->ResetCredentialsInMemory(); + profile_->GetTokenService()->EraseTokensFromDB(); +} + +void SigninManager::OnClientLoginSuccess(const ClientLoginResult& result) { + profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username_); + profile_->GetPrefs()->ScheduleSavePersistentPrefs(); + + GoogleServiceSigninSuccessDetails details(username_, password_); + NotificationService::current()->Notify( + NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, + Source<SigninManager>(this), + Details<const GoogleServiceSigninSuccessDetails>(&details)); + + password_.clear(); // Don't need it anymore. + + profile_->GetTokenService()->UpdateCredentials(result); + DCHECK(profile_->GetTokenService()->AreCredentialsValid()); + profile_->GetTokenService()->StartFetchingTokens(); +} + +void SigninManager::OnClientLoginFailure(const GoogleServiceAuthError& error) { + GoogleServiceAuthError details(error); + NotificationService::current()->Notify( + NotificationType::GOOGLE_SIGNIN_FAILED, + Source<SigninManager>(this), + Details<const GoogleServiceAuthError>(&details)); + SignOut(); +} diff --git a/chrome/browser/sync/signin_manager.h b/chrome/browser/sync/signin_manager.h new file mode 100644 index 0000000..a62514f --- /dev/null +++ b/chrome/browser/sync/signin_manager.h @@ -0,0 +1,81 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// The signin manager encapsulates some functionality tracking +// which user is signed in. When a user is signed in, a ClientLogin +// request is run on their behalf. Auth tokens are fetched from Google +// and the results are stored in the TokenService. + +#ifndef CHROME_BROWSER_SYNC_SIGNIN_MANAGER_H_ +#define CHROME_BROWSER_SYNC_SIGNIN_MANAGER_H_ +#pragma once + +#include <string> +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" + +class GaiaAuthenticator2; +class Profile; +class PrefService; + +// Details for the Notification type GOOGLE_SIGNIN_SUCCESSFUL. +// A listener might use this to make note of a username / password +// pair for encryption keys. +struct GoogleServiceSigninSuccessDetails { + GoogleServiceSigninSuccessDetails(const std::string& in_username, + const std::string& in_password) + : username(in_username), + password(in_password) {} + std::string username; + std::string password; +}; + +class SigninManager : public GaiaAuthConsumer { + public: + // Call to register our prefs. + static void RegisterUserPrefs(PrefService* user_prefs); + + // If user was signed in, load tokens from DB if available. + void Initialize(Profile* profile); + + // If a user is signed in, this will return their name. + // Otherwise, it will return an empty string. + const std::string& GetUsername(); + + // Sets the user name. Used for migrating credentials from previous system. + void SetUsername(const std::string& username); + + // Attempt to sign in this user. If successful, set a preference indicating + // the signed in user and send out a notification, then start fetching tokens + // for the user. + void StartSignIn(const std::string& username, + const std::string& password, + const std::string& login_token, + const std::string& login_captcha); + // Sign a user out, removing the preference, erasing all keys + // associated with the user, and cancelling all auth in progress. + void SignOut(); + + // GaiaAuthConsumer + virtual void OnClientLoginSuccess(const ClientLoginResult& result); + virtual void OnClientLoginFailure(const GoogleServiceAuthError& error); + virtual void OnIssueAuthTokenSuccess(const std::string& service, + const std::string& auth_token) { + NOTREACHED(); + } + virtual void OnIssueAuthTokenFailure(const std::string& service, + const GoogleServiceAuthError& error) { + NOTREACHED(); + } + + private: + Profile* profile_; + std::string username_; + std::string password_; // This is kept empty whenever possible. + scoped_ptr<GaiaAuthenticator2> client_login_; +}; + +#endif // CHROME_BROWSER_SYNC_SIGNIN_MANAGER_H_ diff --git a/chrome/browser/sync/signin_manager_unittest.cc b/chrome/browser/sync/signin_manager_unittest.cc new file mode 100644 index 0000000..208667c --- /dev/null +++ b/chrome/browser/sync/signin_manager_unittest.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/signin_manager.h" + +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/net/gaia/token_service_unittest.h" +#include "chrome/browser/password_manager/encryptor.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/net/test_url_fetcher_factory.h" +#include "chrome/test/testing_profile.h" +#include "chrome/test/signaling_task.h" + +#include "testing/gtest/include/gtest/gtest.h" + +class SigninManagerTest : public TokenServiceTestHarness { + public: + virtual void SetUp() { + TokenServiceTestHarness::SetUp(); + manager_.reset(new SigninManager()); + google_login_success_.ListenFor(NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, + Source<SigninManager>(manager_.get())); + google_login_failure_.ListenFor(NotificationType::GOOGLE_SIGNIN_FAILED, + Source<SigninManager>(manager_.get())); + + URLFetcher::set_factory(&factory_); + } + + TestURLFetcherFactory factory_; + scoped_ptr<SigninManager> manager_; + TestNotificationTracker google_login_success_; + TestNotificationTracker google_login_failure_; +}; + +TEST_F(SigninManagerTest, SignIn) { + manager_->Initialize(profile_.get()); + EXPECT_TRUE(manager_->GetUsername().empty()); + + manager_->StartSignIn("username", "password", "", ""); + EXPECT_FALSE(manager_->GetUsername().empty()); + + // Should go into token service and stop. + manager_->OnClientLoginSuccess(credentials_); + EXPECT_EQ(1U, google_login_success_.size()); + EXPECT_EQ(0U, google_login_failure_.size()); + + // Should persist across resets. + manager_.reset(new SigninManager()); + manager_->Initialize(profile_.get()); + EXPECT_FALSE(manager_->GetUsername().empty()); +} + +TEST_F(SigninManagerTest, SignOut) { + manager_->Initialize(profile_.get()); + manager_->StartSignIn("username", "password", "", ""); + manager_->OnClientLoginSuccess(credentials_); + + EXPECT_FALSE(manager_->GetUsername().empty()); + manager_->SignOut(); + EXPECT_TRUE(manager_->GetUsername().empty()); + // Should not be persisted anymore + manager_.reset(new SigninManager()); + manager_->Initialize(profile_.get()); + EXPECT_TRUE(manager_->GetUsername().empty()); +} + +TEST_F(SigninManagerTest, SignInFailure) { + manager_->Initialize(profile_.get()); + manager_->StartSignIn("username", "password", "", ""); + GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED); + manager_->OnClientLoginFailure(error); + + EXPECT_EQ(0U, google_login_success_.size()); + EXPECT_EQ(1U, google_login_failure_.size()); + + EXPECT_TRUE(manager_->GetUsername().empty()); + + // Should not be persisted + manager_.reset(new SigninManager()); + manager_->Initialize(profile_.get()); + EXPECT_TRUE(manager_->GetUsername().empty()); +} + +TEST_F(SigninManagerTest, SignOutMidConnect) { + manager_->Initialize(profile_.get()); + manager_->StartSignIn("username", "password", "", ""); + manager_->SignOut(); + EXPECT_EQ(0U, google_login_success_.size()); + EXPECT_EQ(0U, google_login_failure_.size()); + + EXPECT_TRUE(manager_->GetUsername().empty()); +} diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc index 5ecd9b6..484d376 100644 --- a/chrome/browser/sync/sync_setup_wizard_unittest.cc +++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc @@ -32,7 +32,7 @@ typedef GoogleServiceAuthError AuthError; class ProfileSyncServiceForWizardTest : public ProfileSyncService { public: ProfileSyncServiceForWizardTest(ProfileSyncFactory* factory, Profile* profile) - : ProfileSyncService(factory, profile, false), + : ProfileSyncService(factory, profile, ""), user_cancelled_dialog_(false) { RegisterPreferences(); ResetTestStats(); diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index c7c1575..32391ae 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc @@ -188,8 +188,8 @@ void OpenSyncMyBookmarksDialog( if (service->HasSyncSetupCompleted()) { ShowOptionsWindow(OPTIONS_PAGE_CONTENT, OPTIONS_GROUP_NONE, profile); } else { - service->EnableForUser(NULL); - ProfileSyncService::SyncEvent(code); + service->ShowLoginDialog(NULL); + ProfileSyncService::SyncEvent(code); // UMA stats } } diff --git a/chrome/browser/sync/syncable/directory_manager.cc b/chrome/browser/sync/syncable/directory_manager.cc index 0719c67..fbdeb46 100644 --- a/chrome/browser/sync/syncable/directory_manager.cc +++ b/chrome/browser/sync/syncable/directory_manager.cc @@ -54,17 +54,6 @@ bool DirectoryManager::Open(const std::string& name) { bool was_open = false; const DirOpenResult result = OpenImpl(name, GetSyncDataDatabasePath(), &was_open); - if (!was_open) { - DirectoryManagerEvent event; - event.dirname = name; - if (syncable::OPENED == result) { - event.what_happened = DirectoryManagerEvent::OPENED; - } else { - event.what_happened = DirectoryManagerEvent::OPEN_FAILED; - event.error = result; - } - channel_->NotifyListeners(event); - } return syncable::OPENED == result; } diff --git a/chrome/browser/sync/syncable/directory_manager.h b/chrome/browser/sync/syncable/directory_manager.h index a6f0847..0c0b55d 100644 --- a/chrome/browser/sync/syncable/directory_manager.h +++ b/chrome/browser/sync/syncable/directory_manager.h @@ -31,14 +31,11 @@ namespace syncable { struct DirectoryManagerEvent { enum { - OPEN_FAILED, - OPENED, CLOSED, CLOSED_ALL, SHUTDOWN, } what_happened; std::string dirname; - DirOpenResult error; // Only for OPEN_FAILED. typedef DirectoryManagerEvent EventType; static inline bool IsChannelShutdownEvent(const EventType& event) { return SHUTDOWN == event.what_happened; diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h index aba7c42..0854a6b 100644 --- a/chrome/browser/sync/test_profile_sync_service.h +++ b/chrome/browser/sync/test_profile_sync_service.h @@ -100,7 +100,7 @@ class SyncBackendHostForProfileSyncTest : public SyncBackendHost { UserShare* user_share = core_->syncapi()->GetUserShare(); DirectoryManager* dir_manager = user_share->dir_manager.get(); - ScopedDirLookup dir(dir_manager, user_share->authenticated_name); + ScopedDirLookup dir(dir_manager, user_share->name); if (!dir.good()) FAIL(); @@ -154,7 +154,6 @@ class SyncBackendHostForProfileSyncTest : public SyncBackendHost { &SyncBackendHost::Core::DoInitializeForTest, user, options.http_bridge_factory, - options.auth_http_bridge_factory, options.delete_sync_data_folder, browser_sync::kDefaultNotificationMethod)); @@ -186,10 +185,12 @@ class TestProfileSyncService : public ProfileSyncService { public: TestProfileSyncService(ProfileSyncFactory* factory, Profile* profile, - bool bootstrap_sync_authentication, + const std::string& test_user, bool synchronous_backend_initialization, Task* initial_condition_setup_task) - : ProfileSyncService(factory, profile, bootstrap_sync_authentication), + : ProfileSyncService(factory, profile, + !test_user.empty() ? + test_user : ""), synchronous_backend_initialization_( synchronous_backend_initialization), synchronous_sync_configuration_(false), diff --git a/chrome/browser/sync/token_migrator.cc b/chrome/browser/sync/token_migrator.cc new file mode 100644 index 0000000..084bc40 --- /dev/null +++ b/chrome/browser/sync/token_migrator.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/token_migrator.h" + +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/util/user_settings.h" +#include "chrome/common/net/gaia/gaia_constants.h" + +const FilePath::CharType kSyncDataFolderName[] = + FILE_PATH_LITERAL("Sync Data"); + +const FilePath::CharType kBookmarkSyncUserSettingsDatabase[] = + FILE_PATH_LITERAL("BookmarkSyncSettings.sqlite3"); + +using browser_sync::UserSettings; + +TokenMigrator::TokenMigrator(ProfileSyncService* service, + const FilePath& profile_path) + : service_(service), + database_location_(profile_path.Append(kSyncDataFolderName)) { +} + +TokenMigrator::~TokenMigrator() { +} + +void TokenMigrator::TryMigration() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, + NewRunnableMethod(this, &TokenMigrator::LoadTokens)); +} + +void TokenMigrator::LoadTokens() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + scoped_ptr<UserSettings> user_settings(new UserSettings()); + FilePath settings_db_file = + database_location_.Append(FilePath(kBookmarkSyncUserSettingsDatabase)); + if (!user_settings->Init(settings_db_file)) + return; + + if (!user_settings->GetLastUserAndServiceToken(GaiaConstants::kSyncService, + &username_, &token_)) + return; + + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &TokenMigrator::PostTokensBack)); +} + +void TokenMigrator::PostTokensBack() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + service_->LoadMigratedCredentials(username_, token_); +} diff --git a/chrome/browser/sync/token_migrator.h b/chrome/browser/sync/token_migrator.h new file mode 100644 index 0000000..6ee3394 --- /dev/null +++ b/chrome/browser/sync/token_migrator.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/file_path.h" +#include "base/task.h" + +class ProfileSyncService; + +// The TokenMigrator provides a bridge between the IO thread and +// the UI thread to load the old-style credentials from the user +// settings DB, and post them back to the UI thread to store the +// token in the token service. +class TokenMigrator { + public: + TokenMigrator(ProfileSyncService* service, + const FilePath& profile_path); + ~TokenMigrator(); + + // This is called on the UI thread, it only posts a task. + // If migration succeeds, the tokens will become available in + // the token service. + void TryMigration(); + + private: + // This runs as a deferred task on the DB thread. + void LoadTokens(); + + // This runs as a deferred task on the UI thread (only after the DB thread is + // finished with the data. + void PostTokensBack(); + + // The username and token retrieved from the user settings DB. + std::string username_; + std::string token_; + + // The ProfileSyncService owns this object, so this pointer is valid when + // PostTokensBack is called. + ProfileSyncService* service_; + + // Pending tasks, stored so they can be canceled if this object is destroyed. + CancelableTask* loading_task_; + + // The directory to search for the user settings database. + FilePath database_location_; + + DISALLOW_COPY_AND_ASSIGN(TokenMigrator); +}; + +// We ensure this object will outlive its tasks, so don't need refcounting. +DISABLE_RUNNABLE_METHOD_REFCOUNT(TokenMigrator); diff --git a/chrome/browser/views/options/content_page_view.cc b/chrome/browser/views/options/content_page_view.cc index 41b9ffa..9af444f 100644 --- a/chrome/browser/views/options/content_page_view.cc +++ b/chrome/browser/views/options/content_page_view.cc @@ -139,7 +139,7 @@ void ContentPageView::ButtonPressed( IDS_CONFIRM_STOP_SYNCING_DIALOG_HEIGHT_LINES))); return; } else { - sync_service_->EnableForUser(GetWindow()->GetNativeWindow()); + sync_service_->ShowLoginDialog(GetWindow()->GetNativeWindow()); ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_OPTIONS); } } else if (sender == sync_customize_button_) { diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 8851192..47e84f4 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -903,10 +903,6 @@ 'browser/sync/engine/all_status.h', 'browser/sync/engine/apply_updates_command.cc', 'browser/sync/engine/apply_updates_command.h', - 'browser/sync/engine/auth_watcher.cc', - 'browser/sync/engine/auth_watcher.h', - 'browser/sync/engine/authenticator.cc', - 'browser/sync/engine/authenticator.h', 'browser/sync/engine/build_and_process_conflict_sets_command.cc', 'browser/sync/engine/build_and_process_conflict_sets_command.h', 'browser/sync/engine/build_commit_command.cc', diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 257b1e0..e6343f3 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2538,18 +2538,18 @@ 'browser/sync/engine/syncapi.h', 'browser/sync/glue/app_data_type_controller.cc', 'browser/sync/glue/app_data_type_controller.h', - 'browser/sync/glue/autofill_change_processor.h', 'browser/sync/glue/autofill_change_processor.cc', + 'browser/sync/glue/autofill_change_processor.h', 'browser/sync/glue/autofill_data_type_controller.cc', 'browser/sync/glue/autofill_data_type_controller.h', - 'browser/sync/glue/autofill_model_associator.h', 'browser/sync/glue/autofill_model_associator.cc', + 'browser/sync/glue/autofill_model_associator.h', 'browser/sync/glue/bookmark_change_processor.cc', 'browser/sync/glue/bookmark_change_processor.h', 'browser/sync/glue/bookmark_data_type_controller.cc', 'browser/sync/glue/bookmark_data_type_controller.h', - 'browser/sync/glue/bookmark_model_associator.h', 'browser/sync/glue/bookmark_model_associator.cc', + 'browser/sync/glue/bookmark_model_associator.h', 'browser/sync/glue/change_processor.cc', 'browser/sync/glue/change_processor.h', 'browser/sync/glue/data_type_controller.h', @@ -2566,16 +2566,14 @@ 'browser/sync/glue/extension_data_type_controller.h', 'browser/sync/glue/extension_model_associator.cc', 'browser/sync/glue/extension_model_associator.h', - 'browser/sync/glue/extension_sync_traits.cc', - 'browser/sync/glue/extension_sync_traits.h', 'browser/sync/glue/extension_sync.cc', 'browser/sync/glue/extension_sync.h', + 'browser/sync/glue/extension_sync_traits.cc', + 'browser/sync/glue/extension_sync_traits.h', 'browser/sync/glue/extension_util.cc', 'browser/sync/glue/extension_util.h', 'browser/sync/glue/history_model_worker.cc', 'browser/sync/glue/history_model_worker.h', - 'browser/sync/glue/password_model_worker.cc', - 'browser/sync/glue/password_model_worker.h', 'browser/sync/glue/http_bridge.cc', 'browser/sync/glue/http_bridge.h', 'browser/sync/glue/model_associator.h', @@ -2585,6 +2583,8 @@ 'browser/sync/glue/password_data_type_controller.h', 'browser/sync/glue/password_model_associator.cc', 'browser/sync/glue/password_model_associator.h', + 'browser/sync/glue/password_model_worker.cc', + 'browser/sync/glue/password_model_worker.h', 'browser/sync/glue/preference_change_processor.cc', 'browser/sync/glue/preference_change_processor.h', 'browser/sync/glue/preference_data_type_controller.cc', @@ -2616,13 +2616,15 @@ 'browser/sync/glue/typed_url_model_associator.h', 'browser/sync/glue/ui_model_worker.cc', 'browser/sync/glue/ui_model_worker.h', - 'browser/sync/notification_method.h', 'browser/sync/notification_method.cc', - 'browser/sync/profile_sync_service.cc', - 'browser/sync/profile_sync_service.h', + 'browser/sync/notification_method.h', 'browser/sync/profile_sync_factory.h', 'browser/sync/profile_sync_factory_impl.cc', 'browser/sync/profile_sync_factory_impl.h', + 'browser/sync/profile_sync_service.cc', + 'browser/sync/profile_sync_service.h', + 'browser/sync/signin_manager.cc', + 'browser/sync/signin_manager.h', 'browser/sync/sync_constants.cc', 'browser/sync/sync_constants.h', 'browser/sync/sync_setup_flow.cc', @@ -2631,8 +2633,10 @@ 'browser/sync/sync_setup_wizard.h', 'browser/sync/sync_ui_util.cc', 'browser/sync/sync_ui_util.h', - 'browser/sync/sync_ui_util_mac.mm', 'browser/sync/sync_ui_util_mac.h', + 'browser/sync/sync_ui_util_mac.mm', + 'browser/sync/token_migrator.cc', + 'browser/sync/token_migrator.h', 'browser/tab_closeable_state_watcher.cc', 'browser/tab_closeable_state_watcher.h', 'browser/tab_contents/background_contents.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index d9c244c..f0470e9 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1147,6 +1147,7 @@ 'browser/net/chrome_url_request_context_unittest.cc', 'browser/net/connection_tester_unittest.cc', 'browser/net/gaia/token_service_unittest.cc', + 'browser/net/gaia/token_service_unittest.h', 'browser/net/load_timing_observer_unittest.cc', 'browser/net/passive_log_collector_unittest.cc', 'browser/net/predictor_unittest.cc', @@ -1263,6 +1264,7 @@ 'browser/sync/profile_sync_service_typed_url_unittest.cc', 'browser/sync/profile_sync_service_unittest.cc', 'browser/sync/profile_sync_test_util.h', + 'browser/sync/signin_manager_unittest.cc', 'browser/sync/sync_setup_wizard_unittest.cc', 'browser/sync/sync_ui_util_mac_unittest.mm', 'browser/sync/test_profile_sync_service.h', @@ -2178,7 +2180,6 @@ 'sources': [ 'app/breakpad_mac_stubs.mm', 'browser/sync/engine/apply_updates_command_unittest.cc', - 'browser/sync/engine/auth_watcher_unittest.cc', 'browser/sync/engine/cleanup_disabled_types_command_unittest.cc', 'browser/sync/engine/download_updates_command_unittest.cc', 'browser/sync/engine/mock_model_safe_workers.h', @@ -2353,7 +2354,6 @@ 'test/live_sync/many_client_live_preferences_sync_test.cc', 'test/live_sync/multiple_client_live_bookmarks_sync_test.cc', 'test/live_sync/multiple_client_live_preferences_sync_test.cc', - 'test/live_sync/offline_sync_test.cc', 'test/live_sync/profile_sync_service_test_harness.cc', 'test/live_sync/profile_sync_service_test_harness.h', 'test/live_sync/single_client_live_bookmarks_sync_test.cc', diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index df520c6..834b9e5 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -1045,6 +1045,13 @@ class NotificationType { // are a ChromeCookieDetails object. COOKIE_CHANGED, + // Sidebar ----------------------------------------------------------------- + + // Sent when the sidebar state is changed. + // The source is a SidebarManager instance, the details are the changed + // SidebarContainer object. + SIDEBAR_CHANGED, + // Token Service ----------------------------------------------------------- // When the token service has a new token available for a service, one of @@ -1053,16 +1060,32 @@ class NotificationType { // TokenAvailableDetails object. TOKEN_AVAILABLE, - // Sent when the sidebar state is changed. - // The source is a SidebarManager instance, the details are the changed - // SidebarContainer object. - SIDEBAR_CHANGED, + // When there aren't any additional tokens left to load, this notification + // is sent. + // The source is a TokenService on the profile. There are no details. + TOKEN_LOADING_FINISHED, // If a token request failed, one of these is issued per failed request. // The source is a TokenService on the Profile. The details are a // TokenRequestFailedDetails object. TOKEN_REQUEST_FAILED, + // When a service has a new token they got from a frontend that the + // TokenService should know about, fire this notification. The details + // are a TokenAvailableDetails object. + TOKEN_UPDATED, + + // Sent when a user signs into Google services such as sync. + // The source is the SigninManager instance. The details are a + // GoogleServiceSignin object. + GOOGLE_SIGNIN_SUCCESSFUL, + + // Sent when a user fails to sign into Google services such as sync. + // The source is the SigninManager instance. The details are a + // GoogleServiceAuthError object. + GOOGLE_SIGNIN_FAILED, + + // Misc -------------------------------------------------------------------- #if defined(OS_CHROMEOS) diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 2cd8b84..168f4e8 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -923,10 +923,17 @@ const char kSyncManaged[] = "sync.managed"; // used when sync is disabled by the user via the privacy dashboard. const char kSyncSuppressStart[] = "sync.suppress_start"; +// Boolean to reperesent if sync credentials have been migrated from the +// user settings DB to the token service. +const char kSyncCredentialsMigrated[] = "sync.credentials_migrated"; + // A string that can be used to restore sync encryption infrastructure on // startup so that the user doesn't need to provide credentials on each start. const char kEncryptionBootstrapToken[] = "sync.encryption_bootstrap_token"; +// String that identifies the user logged into sync and other google services. +const char kGoogleServicesUsername[] = "google.services.username"; + // Create web application shortcut dialog preferences. const char kWebAppCreateOnDesktop[] = "browser.web_app.create_on_desktop"; const char kWebAppCreateInAppsMenu[] = "browser.web_app.create_in_apps_menu"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 6321c6a..1caaa31b 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -342,6 +342,8 @@ extern const char kSyncTypedUrls[]; extern const char kSyncExtensions[]; extern const char kSyncManaged[]; extern const char kSyncSuppressStart[]; +extern const char kGoogleServicesUsername[]; +extern const char kSyncCredentialsMigrated[]; extern const char kEncryptionBootstrapToken[]; extern const char kWebAppCreateOnDesktop[]; diff --git a/chrome/test/live_sync/live_sync_test.cc b/chrome/test/live_sync/live_sync_test.cc index 5ff3e74..9fc8598 100644 --- a/chrome/test/live_sync/live_sync_test.cc +++ b/chrome/test/live_sync/live_sync_test.cc @@ -22,6 +22,7 @@ #include "chrome/test/testing_browser_process.h" #include "googleurl/src/gurl.h" #include "net/base/escape.h" +#include "net/base/network_change_notifier.h" #include "net/proxy/proxy_config.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/proxy/proxy_service.h" @@ -231,6 +232,10 @@ void LiveSyncTest::SetUpInProcessBrowserTestFixture() { // Without this, running the test case on Linux causes an error as we make an // external DNS lookup of "ocsp.thawte.com". resolver->AllowDirectLookup("*.thawte.com"); + // The new XMPP cert seems to use crl.geotrust.com on Linux. + resolver->AllowDirectLookup("*.geotrust.com"); + // SSL chain. + resolver->AllowDirectLookup("*.gstatic.com"); mock_host_resolver_override_.reset( new net::ScopedDefaultHostResolverProc(resolver)); } @@ -255,10 +260,12 @@ void LiveSyncTest::TearDownLocalTestServer() { ASSERT_TRUE(test_server_.Stop()); } - void LiveSyncTest::EnableNetwork(Profile* profile) { SetProxyConfig(profile->GetRequestContext(), net::ProxyConfig::CreateDirect()); + // Bugs: http://crbug.com/53857 + // http://crbug.com/53858 + net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); } void LiveSyncTest::DisableNetwork(Profile* profile) { @@ -267,6 +274,7 @@ void LiveSyncTest::DisableNetwork(Profile* profile) { net::ProxyConfig config; config.proxy_rules().ParseFromString("http=127.0.0.1:0"); SetProxyConfig(profile->GetRequestContext(), config); + net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); } bool LiveSyncTest::EnsureSyncServerConfiguration() { diff --git a/chrome/test/live_sync/offline_sync_test.cc b/chrome/test/live_sync/offline_sync_test.cc deleted file mode 100644 index c64df34..0000000 --- a/chrome/test/live_sync/offline_sync_test.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/profile.h" -#include "chrome/common/net/gaia/google_service_auth_error.h" -#include "chrome/test/live_sync/live_sync_test.h" -#include "chrome/test/live_sync/profile_sync_service_test_harness.h" -#include "testing/gmock/include/gmock/gmock.h" - -class OfflineSyncTest : public LiveSyncTest { - public: - OfflineSyncTest() : LiveSyncTest(LiveSyncTest::SINGLE_CLIENT) {} - - private: - DISALLOW_COPY_AND_ASSIGN(OfflineSyncTest); -}; - -IN_PROC_BROWSER_TEST_F(OfflineSyncTest, OfflineStart) { - ASSERT_TRUE(SetupClients()) << "SetupClients() failed."; - DisableNetwork(GetProfile(0)); - - EXPECT_FALSE(GetClient(0)->SetupSync()); - EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, - GetClient(0)->service()->GetAuthError().state()); - - EnableNetwork(GetProfile(0)); - EXPECT_TRUE(GetClient(0)->RetryAuthentication()); -} diff --git a/chrome/test/live_sync/profile_sync_service_test_harness.cc b/chrome/test/live_sync/profile_sync_service_test_harness.cc index 12448da..b49bf56 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.cc +++ b/chrome/test/live_sync/profile_sync_service_test_harness.cc @@ -7,9 +7,11 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/sync/glue/sync_backend_host.h" #include "chrome/browser/sync/sessions/session_state.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "chrome/common/pref_names.h" #include "chrome/test/live_sync/profile_sync_service_test_harness.h" @@ -84,7 +86,8 @@ bool StateChangeTimeoutEvent::Abort() { ProfileSyncServiceTestHarness::ProfileSyncServiceTestHarness(Profile* p, const std::string& username, const std::string& password, int id) - : wait_state_(WAITING_FOR_ON_AUTH_ERROR), profile_(p), service_(NULL), + : wait_state_(WAITING_FOR_ON_BACKEND_INITIALIZED), + profile_(p), service_(NULL), last_status_(kInvalidStatus), last_timestamp_(0), min_timestamp_needed_(kMinTimestampNeededNone), @@ -95,15 +98,11 @@ ProfileSyncServiceTestHarness::ProfileSyncServiceTestHarness(Profile* p, } bool ProfileSyncServiceTestHarness::SetupSync() { - service_ = profile_->GetProfileSyncService(); - service_->StartUp(); + service_ = profile_->GetProfileSyncService(""); service_->AddObserver(this); - return WaitForServiceInit(false); -} + service_->signin_.StartSignIn(username_, password_, "", ""); -bool ProfileSyncServiceTestHarness::RetryAuthentication() { - wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED; - return WaitForServiceInit(true); + return WaitForServiceInit(); } void ProfileSyncServiceTestHarness::SignalStateCompleteWithNextState( @@ -121,11 +120,6 @@ bool ProfileSyncServiceTestHarness::RunStateChangeMachine() { WaitState state = wait_state_; ProfileSyncService::Status status(service_->QueryDetailedSyncStatus()); switch (wait_state_) { - case WAITING_FOR_ON_AUTH_ERROR: { - LogClientInfo("WAITING_FOR_ON_AUTH_ERROR"); - SignalStateCompleteWithNextState(WAITING_FOR_ON_BACKEND_INITIALIZED); - break; - } case WAITING_FOR_ON_BACKEND_INITIALIZED: { LogClientInfo("WAITING_FOR_ON_BACKEND_INITIALIZED"); if (service_->GetAuthError().state() != GoogleServiceAuthError::NONE) { @@ -143,6 +137,21 @@ bool ProfileSyncServiceTestHarness::RunStateChangeMachine() { } break; } + case WAITING_FOR_SERVER_REACHABLE: { + LogClientInfo("WAITING_FOR_SERVER_REACHABLE"); + const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); + if (!status.server_reachable) { + break; + } + if (service()->backend()->HasUnsyncedItems() || + snap->has_more_to_sync || snap->unsynced_count != 0) { + SignalStateCompleteWithNextState(WAITING_FOR_SYNC_TO_FINISH); + break; + } + last_timestamp_ = snap->max_local_timestamp; + SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } case WAITING_FOR_SYNC_TO_FINISH: { LogClientInfo("WAITING_FOR_SYNC_TO_FINISH"); const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); @@ -151,7 +160,10 @@ bool ProfileSyncServiceTestHarness::RunStateChangeMachine() { // be a sufficient condition for sync to have completed. However, the // additional check of snap->unsynced_count is required due to // http://crbug.com/48989. - if (snap->has_more_to_sync || snap->unsynced_count != 0) { + if (service()->backend()->HasUnsyncedItems() || + snap->has_more_to_sync || snap->unsynced_count != 0) { + if (!status.server_reachable) + SignalStateCompleteWithNextState(WAITING_FOR_SERVER_REACHABLE); break; } EXPECT_LE(last_timestamp_, snap->max_local_timestamp); @@ -286,18 +298,10 @@ bool ProfileSyncServiceTestHarness::AwaitStatusChangeWithTimeout( return timeout_signal->Abort(); } -bool ProfileSyncServiceTestHarness::WaitForServiceInit(bool is_auth_retry) { +bool ProfileSyncServiceTestHarness::WaitForServiceInit() { LogClientInfo("WaitForServiceInit"); - if (!is_auth_retry) { - // Wait for the OnAuthError() callback. - EXPECT_EQ(wait_state_, WAITING_FOR_ON_AUTH_ERROR); - EXPECT_TRUE(AwaitStatusChangeWithTimeout(30, - "Waiting for the OnAuthError() callback.")) << - "OnAuthError() not seen after 30 seconds."; - } - // Enter GAIA credentials and wait for the OnBackendInitialized() callback. - service_->backend()->Authenticate(username_, password_, std::string()); + // Wait for the OnBackendInitialized() callback. EXPECT_EQ(wait_state_, WAITING_FOR_ON_BACKEND_INITIALIZED); EXPECT_TRUE(AwaitStatusChangeWithTimeout(30, "Waiting for OnBackendInitialized().")) << @@ -307,15 +311,13 @@ bool ProfileSyncServiceTestHarness::WaitForServiceInit(bool is_auth_retry) { return false; } - // Choose datatypes to be synced. Note: This is unnecessary on Chrome OS. - if (!browser_defaults::kBootstrapSyncAuthentication) { - syncable::ModelTypeSet set; - for (int i = syncable::FIRST_REAL_MODEL_TYPE; - i < syncable::MODEL_TYPE_COUNT; ++i) { - set.insert(syncable::ModelTypeFromInt(i)); - } - service_->OnUserChoseDatatypes(true, set); + // Choose datatypes to be synced. + syncable::ModelTypeSet set; + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + set.insert(syncable::ModelTypeFromInt(i)); } + service_->OnUserChoseDatatypes(true, set); // Wait for notifications_enabled to be set to true. EXPECT_EQ(wait_state_, WAITING_FOR_NOTIFICATIONS_ENABLED); @@ -329,7 +331,10 @@ bool ProfileSyncServiceTestHarness::WaitForServiceInit(bool is_auth_retry) { const SyncSessionSnapshot* ProfileSyncServiceTestHarness::GetLastSessionSnapshot() const { EXPECT_FALSE(service_ == NULL) << "Sync service has not yet been set up."; - return service_->backend()->GetLastSessionSnapshot(); + if (service_->backend()) { + return service_->backend()->GetLastSessionSnapshot(); + } + return NULL; } void ProfileSyncServiceTestHarness::LogClientInfo(std::string message) { diff --git a/chrome/test/live_sync/profile_sync_service_test_harness.h b/chrome/test/live_sync/profile_sync_service_test_harness.h index 3b32930..34cfde3 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.h +++ b/chrome/test/live_sync/profile_sync_service_test_harness.h @@ -31,10 +31,6 @@ class ProfileSyncServiceTestHarness : public ProfileSyncServiceObserver { // authenticated, and we are ready to process changes. bool SetupSync(); - // Retries a sync setup when the previous attempt was aborted by an - // authentication failure. - bool RetryAuthentication(); - // ProfileSyncServiceObserver implementation. virtual void OnStateChanged(); @@ -79,16 +75,16 @@ class ProfileSyncServiceTestHarness : public ProfileSyncServiceObserver { friend class StateChangeTimeoutEvent; enum WaitState { - // The sync client awaits the OnAuthError() callback. - WAITING_FOR_ON_AUTH_ERROR = 0, // The sync client awaits the OnBackendInitialized() callback. - WAITING_FOR_ON_BACKEND_INITIALIZED, + WAITING_FOR_ON_BACKEND_INITIALIZED = 0, // The sync client is waiting for notifications_enabled to become true. WAITING_FOR_NOTIFICATIONS_ENABLED, // The sync client is waiting for an ongoing sync cycle to complete. WAITING_FOR_SYNC_TO_FINISH, // The sync client anticipates incoming updates leading to a new sync cycle. WAITING_FOR_UPDATES, + // The sync client is waiting for server_reachable to become true. + WAITING_FOR_SERVER_REACHABLE, // The sync client is fully synced and there are no pending updates. FULLY_SYNCED, // An authentication error has occurred. @@ -110,7 +106,7 @@ class ProfileSyncServiceTestHarness : public ProfileSyncServiceObserver { // Returns true if the service initialized correctly. Set is_auth_retry to // true when calling this method second time after an authentication failure. - bool WaitForServiceInit(bool is_auth_retry); + bool WaitForServiceInit(); // Logs message with relevant info about client's sync state (if available). void LogClientInfo(std::string message); diff --git a/chrome/test/live_sync/single_client_live_bookmarks_sync_test.cc b/chrome/test/live_sync/single_client_live_bookmarks_sync_test.cc index c4cc585..bc66917 100644 --- a/chrome/test/live_sync/single_client_live_bookmarks_sync_test.cc +++ b/chrome/test/live_sync/single_client_live_bookmarks_sync_test.cc @@ -4,6 +4,37 @@ #include "chrome/test/live_sync/live_bookmarks_sync_test.h" +// TODO(rsimha): Debug the flakiness of this with fred. http://crbug.com/53858 +IN_PROC_BROWSER_TEST_F(SingleClientLiveBookmarksSyncTest, + FAILS_OfflineToOnline) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + + DisableNetwork(GetProfile(0)); + + BookmarkModel* bm = GetBookmarkModel(0); + BookmarkModelVerifier* v = verifier_helper(); + + const BookmarkNode* top = v->AddGroup(bm, bm->other_node(), 0, L"top"); + v->SetTitle(bm, top, L"title"); + + + EXPECT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Offline state change.")); + { + ProfileSyncService::Status status( + GetClient(0)->service()->QueryDetailedSyncStatus()); + EXPECT_EQ(status.summary, ProfileSyncService::Status::OFFLINE_UNSYNCED); + } + + EnableNetwork(GetProfile(0)); + { + EXPECT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Commit changes.")); + ProfileSyncService::Status status( + GetClient(0)->service()->QueryDetailedSyncStatus()); + EXPECT_EQ(status.summary, ProfileSyncService::Status::READY); + v->ExpectMatch(bm); + } +} + IN_PROC_BROWSER_TEST_F(SingleClientLiveBookmarksSyncTest, Sanity) { ASSERT_TRUE(SetupClients()) << "SetupClients() failed."; BookmarkModel* bm = GetBookmarkModel(0); diff --git a/chrome/test/profile_mock.h b/chrome/test/profile_mock.h index 67f7ee2..b3b0dcf 100644 --- a/chrome/test/profile_mock.h +++ b/chrome/test/profile_mock.h @@ -18,6 +18,7 @@ class ProfileMock : public TestingProfile { MOCK_METHOD1(GetWebDataService, WebDataService*(ServiceAccessType access)); MOCK_METHOD0(GetPersonalDataManager, PersonalDataManager*()); MOCK_METHOD1(GetPasswordStore, PasswordStore* (ServiceAccessType access)); + MOCK_METHOD0(GetTokenService, TokenService*()); }; #endif // CHROME_TEST_PROFILE_MOCK_H__ diff --git a/chrome/test/sync/engine/mock_connection_manager.cc b/chrome/test/sync/engine/mock_connection_manager.cc index bfba668..24ad1f2 100644 --- a/chrome/test/sync/engine/mock_connection_manager.cc +++ b/chrome/test/sync/engine/mock_connection_manager.cc @@ -33,7 +33,7 @@ using syncable::WriteTransaction; MockConnectionManager::MockConnectionManager(DirectoryManager* dirmgr, const string& name) - : ServerConnectionManager("unused", 0, false, "version", "id"), + : ServerConnectionManager("unused", 0, false, "version"), conflict_all_commits_(false), conflict_n_commits_(0), next_new_id_(10000), diff --git a/chrome/test/sync/engine/syncer_command_test.h b/chrome/test/sync/engine/syncer_command_test.h index eed8def..02ea57d 100644 --- a/chrome/test/sync/engine/syncer_command_test.h +++ b/chrome/test/sync/engine/syncer_command_test.h @@ -48,6 +48,9 @@ class SyncerCommandTestWithParam : public testing::TestWithParam<T>, virtual void OnShouldStopSyncingPermanently() { FAIL() << "Shouldn't be forced to stop syncing."; } + virtual void SignalUpdatedToken(const std::string& token) { + FAIL() << "Should never update token."; + } // ModelSafeWorkerRegistrar implementation. virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) { @@ -91,7 +94,7 @@ class SyncerCommandTestWithParam : public testing::TestWithParam<T>, void ResetContext() { context_.reset(new sessions::SyncSessionContext( - mock_server_.get(), NULL, syncdb_->manager(), registrar())); + mock_server_.get(), syncdb_->manager(), registrar())); context_->set_account_name(syncdb_->name()); ClearSession(); } diff --git a/chrome/test/testing_profile.cc b/chrome/test/testing_profile.cc index c6ad9b3..6fd04fc 100644 --- a/chrome/test/testing_profile.cc +++ b/chrome/test/testing_profile.cc @@ -453,6 +453,11 @@ TokenService* TestingProfile::GetTokenService() { } ProfileSyncService* TestingProfile::GetProfileSyncService() { + return GetProfileSyncService(""); +} + +ProfileSyncService* TestingProfile::GetProfileSyncService( + const std::string& cros_user) { if (!profile_sync_service_.get()) { // Use a NiceMock here since we are really using the mock as a // fake. Test cases that want to set expectations on a diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index 6399692..d75b7c1 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -224,6 +224,7 @@ class TestingProfile : public Profile { virtual void InitExtensions() {} virtual void InitWebResources() {} virtual NTPResourceCache* GetNTPResourceCache(); + virtual DesktopNotificationService* GetDesktopNotificationService(); virtual BackgroundContentsService* GetBackgroundContentsService() { return NULL; @@ -252,6 +253,8 @@ class TestingProfile : public Profile { // Creates and initializes a profile sync service if the tests require one. virtual TokenService* GetTokenService(); virtual ProfileSyncService* GetProfileSyncService(); + virtual ProfileSyncService* GetProfileSyncService( + const std::string& cros_notes); virtual CloudPrintProxyService* GetCloudPrintProxyService() { return NULL; } virtual ChromeBlobStorageContext* GetBlobStorageContext() { |