diff options
Diffstat (limited to 'chrome')
81 files changed, 2035 insertions, 1314 deletions
diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index c371880..9ec92d02 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -181,10 +181,6 @@ 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 58df847..a2ab751 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_->ShowLoginDialog(NULL); + syncService_->EnableForUser(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 3ecd267..6970816 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_->ShowLoginDialog(NULL); + sync_service_->EnableForUser(NULL); } } diff --git a/chrome/browser/gtk/options/content_page_gtk.cc b/chrome/browser/gtk/options/content_page_gtk.cc index 9d6d72b..13b2cea 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_->ShowLoginDialog(NULL); + sync_service_->EnableForUser(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 b61ab62..b46868f 100644 --- a/chrome/browser/net/gaia/token_service.cc +++ b/chrome/browser/net/gaia/token_service.cc @@ -13,8 +13,6 @@ #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() @@ -31,18 +29,12 @@ 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() { @@ -155,12 +147,6 @@ 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)); @@ -192,11 +178,6 @@ 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. @@ -226,12 +207,3 @@ 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 006423f..20ca12d 100644 --- a/chrome/browser/net/gaia/token_service.h +++ b/chrome/browser/net/gaia/token_service.h @@ -37,15 +37,12 @@ #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 "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" +#include "base/gtest_prod_util.h" class URLRequestContextGetter; class Profile; @@ -53,8 +50,7 @@ class Profile; // The TokenService is a Profile member, so all calls are expected // from the UI thread. class TokenService : public GaiaAuthConsumer, - public WebDataServiceConsumer, - public NotificationObserver { + public WebDataServiceConsumer { public: TokenService(); virtual ~TokenService(); @@ -127,10 +123,6 @@ 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); @@ -141,11 +133,6 @@ 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, @@ -182,8 +169,6 @@ 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 ce0096b..512efff 100644 --- a/chrome/browser/net/gaia/token_service_unittest.cc +++ b/chrome/browser/net/gaia/token_service_unittest.cc @@ -4,18 +4,127 @@ // // This file defines a unit test for the profile's token service. -#include "chrome/browser/net/gaia/token_service_unittest.h" - +#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/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(); + } + } -class TokenServiceTest : public TokenServiceTestHarness { + TokenService::TokenRequestFailedDetails details_; +}; + +class TokenServiceTest : public testing::Test { public: + TokenServiceTest() + : ui_thread_(ChromeThread::UI, &message_loop_), + db_thread_(ChromeThread::DB) { + } + virtual void SetUp() { - TokenServiceTestHarness::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()); 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) { @@ -36,7 +145,8 @@ TEST_F(TokenServiceTest, NotificationSuccess) { EXPECT_EQ(1U, success_tracker_.size()); EXPECT_EQ(0U, failure_tracker_.size()); - TokenService::TokenAvailableDetails details = success_tracker_.details(); + TokenService::TokenAvailableDetails details = + success_tracker_.get_last_token_details(); // MSVC doesn't like this comparison as EQ. EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); EXPECT_EQ(details.token(), "token"); @@ -50,7 +160,8 @@ TEST_F(TokenServiceTest, NotificationFailed) { EXPECT_EQ(0U, success_tracker_.size()); EXPECT_EQ(1U, failure_tracker_.size()); - TokenService::TokenRequestFailedDetails details = failure_tracker_.details(); + TokenService::TokenRequestFailedDetails details = + failure_tracker_.get_last_token_details(); // MSVC doesn't like this comparison as EQ. EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); @@ -164,7 +275,8 @@ TEST_F(TokenServiceTest, LoadTokensIntoMemoryBasic) { service_.LoadTokensIntoMemory(db_tokens, &memory_tokens); EXPECT_EQ(1U, success_tracker_.size()); - TokenService::TokenAvailableDetails details = success_tracker_.details(); + TokenService::TokenAvailableDetails details = + success_tracker_.get_last_token_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 deleted file mode 100644 index ad46138..0000000 --- a/chrome/browser/net/gaia/token_service_unittest.h +++ /dev/null @@ -1,130 +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. -// -// 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 075cbf1..2b37c26 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -42,7 +42,6 @@ #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" @@ -140,7 +139,6 @@ 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 b2d7eba..5c3669c 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -444,11 +444,6 @@ 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 68eec93..d876d8c 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -377,11 +377,6 @@ 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 ff6a8da..6a7cb51 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -479,7 +479,6 @@ 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 @@ -1236,16 +1235,10 @@ 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(cros_user); + InitSyncService(); return sync_service_.get(); } @@ -1255,11 +1248,11 @@ CloudPrintProxyService* ProfileImpl::GetCloudPrintProxyService() { return cloud_print_proxy_service_.get(); } -void ProfileImpl::InitSyncService(const std::string& cros_user) { +void ProfileImpl::InitSyncService() { profile_sync_factory_.reset( new ProfileSyncFactoryImpl(this, CommandLine::ForCurrentProcess())); sync_service_.reset( - profile_sync_factory_->CreateProfileSyncService(cros_user)); + profile_sync_factory_->CreateProfileSyncService()); sync_service_->Initialize(); } diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h index 79e4438..d3831cb 100644 --- a/chrome/browser/profile_impl.h +++ b/chrome/browser/profile_impl.h @@ -100,10 +100,8 @@ 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(const std::string& cros_user); + void InitSyncService(); 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 73d59ef..7f81ca2 100644 --- a/chrome/browser/sync/abstract_profile_sync_service_test.h +++ b/chrome/browser/sync/abstract_profile_sync_service_test.h @@ -12,7 +12,6 @@ #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" @@ -56,7 +55,7 @@ class ProfileSyncServiceTestHelper { UserShare* user_share = service->backend()->GetUserShareHandle(); DirectoryManager* dir_manager = user_share->dir_manager.get(); - ScopedDirLookup dir(dir_manager, user_share->name); + ScopedDirLookup dir(dir_manager, user_share->authenticated_name); if (!dir.good()) return false; @@ -121,7 +120,6 @@ 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 72e1f6d..178fe7f 100644 --- a/chrome/browser/sync/engine/all_status.cc +++ b/chrome/browser/sync/engine/all_status.cc @@ -8,6 +8,7 @@ #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" @@ -47,6 +48,11 @@ 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)); @@ -93,6 +99,10 @@ 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; @@ -143,6 +153,34 @@ 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) { @@ -184,15 +222,6 @@ 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 b843a1a..772f40d 100644 --- a/chrome/browser/sync/engine/all_status.h +++ b/chrome/browser/sync/engine/all_status.h @@ -89,6 +89,7 @@ class AllStatus : public ChannelEventHandler<SyncerEvent> { AllStatus(); ~AllStatus(); + void WatchConnectionManager(ServerConnectionManager* conn_mgr); void HandleServerConnectionEvent(const ServerConnectionEvent& event); void HandleAuthWatcherEvent(const AuthWatcherEvent& event); @@ -115,6 +116,7 @@ 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; @@ -123,6 +125,7 @@ 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 new file mode 100644 index 0000000..b414e18 --- /dev/null +++ b/chrome/browser/sync/engine/auth_watcher.cc @@ -0,0 +1,352 @@ +// 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 new file mode 100644 index 0000000..da90f95 --- /dev/null +++ b/chrome/browser/sync/engine/auth_watcher.h @@ -0,0 +1,222 @@ +// 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 new file mode 100644 index 0000000..cc840c7 --- /dev/null +++ b/chrome/browser/sync/engine/auth_watcher_unittest.cc @@ -0,0 +1,235 @@ +// 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 new file mode 100644 index 0000000..eec7af3 --- /dev/null +++ b/chrome/browser/sync/engine/authenticator.cc @@ -0,0 +1,110 @@ +// 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 new file mode 100644 index 0000000..1abca3c --- /dev/null +++ b/chrome/browser/sync/engine/authenticator.h @@ -0,0 +1,105 @@ +// 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 6d35811..4953836 100644 --- a/chrome/browser/sync/engine/net/server_connection_manager.cc +++ b/chrome/browser/sync/engine/net/server_connection_manager.cc @@ -135,9 +135,11 @@ ServerConnectionManager::ServerConnectionManager( const string& server, int port, bool use_ssl, - const string& user_agent) + const string& user_agent, + const string& client_id) : sync_server_(server), sync_server_port_(port), + client_id_(client_id), user_agent_(user_agent), use_ssl_(use_ssl), proto_sync_path_(kSyncServerSyncPath), @@ -161,6 +163,7 @@ void ServerConnectionManager::NotifyStatusChanged() { channel_->NotifyListeners(event); } +// Uses currently set auth token. Set by AuthWatcher. bool ServerConnectionManager::PostBufferWithCachedAuth( const PostBufferParams* params, ScopedServerStatusWatcher* watcher) { string path = @@ -168,6 +171,14 @@ 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 526dc8e..602116a 100644 --- a/chrome/browser/sync/engine/net/server_connection_manager.h +++ b/chrome/browser/sync/engine/net/server_connection_manager.h @@ -213,7 +213,8 @@ class ServerConnectionManager { ServerConnectionManager(const std::string& server, int port, bool use_ssl, - const std::string& user_agent); + const std::string& user_agent, + const std::string& client_id); virtual ~ServerConnectionManager(); @@ -224,6 +225,14 @@ 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); @@ -280,11 +289,6 @@ 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_); @@ -329,7 +333,7 @@ class ServerConnectionManager { int sync_server_port_; // The unique id of the user's client. - std::string client_id_; + const 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 4108f9a..67424b9 100644 --- a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h +++ b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h @@ -51,8 +51,10 @@ 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), + : ServerConnectionManager(server, port, use_ssl, client_version, + client_id), 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 15e0e8d..00d1c4f 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -25,6 +25,7 @@ #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" @@ -48,6 +49,7 @@ #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" @@ -59,15 +61,17 @@ 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; @@ -906,6 +910,7 @@ 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), @@ -924,25 +929,40 @@ 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 SyncCredentials& credentials, + const std::string& lsid, const bool use_chrome_async_socket, const bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, const std::string& restored_key_for_bootstrapping); - - // 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 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); // Tell the sync engine to start the syncing process. void StartSyncing(); @@ -967,17 +987,20 @@ class SyncManager::SyncInternal // This listener is called by the syncer channel for all syncer events. virtual void HandleChannelEvent(const SyncerEvent& event); - // Listens for notifications from the ServerConnectionManager - void HandleServerConnectionEvent(const ServerConnectionEvent& 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); - // Open the directory named with username_for_share - bool OpenDirectory(); + // Listen here for directory opened events. + void HandleDirectoryManagerEvent( + const syncable::DirectoryManagerEvent& event); // 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); @@ -993,13 +1016,14 @@ 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_.name; + return share_.authenticated_name; } // Note about SyncManager::Status implementation: Status is a trimmed @@ -1054,6 +1078,21 @@ 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(); @@ -1132,6 +1171,10 @@ 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. @@ -1152,6 +1195,13 @@ 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 @@ -1163,12 +1213,20 @@ 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_; @@ -1203,11 +1261,17 @@ 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 SyncCredentials& credentials, + const char* lsid, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -1218,22 +1282,29 @@ 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, - credentials, + lsid, use_chrome_async_socket, try_ssltcp_first, notification_method, restored_key_for_bootstrapping); } -void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { - data_->UpdateCredentials(credentials); +void SyncManager::Authenticate(const char* username, const char* password, + const char* captcha) { + data_->Authenticate(std::string(username), std::string(password), + std::string(captcha)); } - bool SyncManager::InitialSyncEndedForAllEnabledTypes() { return data_->InitialSyncEndedForAllEnabledTypes(); } @@ -1258,7 +1329,6 @@ 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(); @@ -1268,11 +1338,17 @@ 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 SyncCredentials& credentials, + const std::string& lsid, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -1283,16 +1359,32 @@ 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, post_factory)); + sync_server_and_path, port, use_ssl, user_agent, client_id, + post_factory)); - connection_manager_hookup_.reset( - NewEventListenerHookup(connection_manager()->channel(), this, - &SyncManager::SyncInternal::HandleServerConnectionEvent)); + // Watch various objects for aggregated status. + allstatus_.WatchConnectionManager(connection_manager()); net::NetworkChangeNotifier::AddObserver(this); // TODO(akalin): CheckServerReachable() can block, which may cause jank if we @@ -1312,7 +1404,7 @@ bool SyncManager::SyncInternal::Init( const bool kInitializeSsl = true; const bool kConnectImmediately = false; talk_mediator_.reset(new TalkMediatorImpl(mediator_thread, kInitializeSsl, - kConnectImmediately, false)); + kConnectImmediately, invalidate_xmpp_auth_token)); if (notification_method != browser_sync::NOTIFICATION_LEGACY && notification_method != browser_sync::NOTIFICATION_SERVER) { if (notification_method == browser_sync::NOTIFICATION_TRANSITIONAL) { @@ -1325,11 +1417,30 @@ bool SyncManager::SyncInternal::Init( // Listen to TalkMediator events ourselves talk_mediator_->SetDelegate(this); - LOG(INFO) << "Sync is bringing up SyncSessionContext."; + 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)); // Build a SyncSessionContext and store the worker in it. SyncSessionContext* context = new SyncSessionContext( - connection_manager_.get(), dir_manager(), model_safe_worker_registrar); + connection_manager_.get(), auth_watcher(), + dir_manager(), model_safe_worker_registrar); // The SyncerThread takes ownership of |context|. syncer_thread_ = new SyncerThread(context); @@ -1338,7 +1449,22 @@ bool SyncManager::SyncInternal::Init( // Subscribe to the syncer thread's channel. syncer_event_.reset(syncer_thread()->relay_channel()->AddObserver(this)); - return SignIn(credentials); + 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; } void SyncManager::SyncInternal::StartSyncing() { @@ -1410,62 +1536,53 @@ void SyncManager::SyncInternal::SendPendingXMPPNotification( } } -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; +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(); } + auth_watcher()->Authenticate(username, password, std::string(), + captcha); +} - connection_manager()->set_client_id(lookup->cache_guid()); - syncer_thread()->CreateSyncer(username_for_share()); - MarkAndNotifyInitializationComplete(); - dir_change_hookup_.reset(lookup->AddChangeObserver(this)); - return true; +void SyncManager::SyncInternal::AuthenticateWithLsid(const string& lsid) { + DCHECK(!lsid.empty()); + auth_watcher()->AuthenticateWithLsid(lsid); } -bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { - DCHECK_EQ(MessageLoop::current(), core_message_loop_); - DCHECK(share_.name.empty()); - share_.name = credentials.email; +bool SyncManager::SyncInternal::AuthenticateForUser( + const std::string& username, const std::string& auth_token) { + share_.authenticated_name = username; - LOG(INFO) << "Signing in user: " << username_for_share(); - if (!OpenDirectory()) { + // 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(); return false; } - UpdateCredentials(credentials); + // 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); 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() { - if (observer_) { - observer_->OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)); - } + auth_problem_ = AuthError::INVALID_GAIA_CREDENTIALS; + if (observer_) + observer_->OnAuthError(AuthError(auth_problem_)); } void SyncManager::SyncInternal::SetPassphrase( @@ -1525,6 +1642,15 @@ 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..."; @@ -1533,6 +1659,15 @@ 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."; @@ -1544,7 +1679,7 @@ void SyncManager::SyncInternal::Shutdown() { // Pump any messages the auth watcher, syncer thread, or talk // mediator posted before they shut down. (See HandleSyncerEvent(), - // and HandleTalkMediatorEvent() for the + // HandleAuthWatcherEvent(), and HandleTalkMediatorEvent() for the // events that may be posted.) { CHECK(core_message_loop_); @@ -1556,8 +1691,6 @@ void SyncManager::SyncInternal::Shutdown() { net::NetworkChangeNotifier::RemoveObserver(this); - connection_manager_hookup_.reset(); - if (dir_manager()) { dir_manager()->FinalSaveChangesForAll(); dir_manager()->Close(username_for_share()); @@ -1566,6 +1699,7 @@ 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(); @@ -1578,7 +1712,22 @@ 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(); - sync_manager_->RequestNudge(); +} + +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(); + } } // Listen to model changes, filter out ones initiated by the sync API, and @@ -1601,25 +1750,6 @@ 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 @@ -1851,11 +1981,105 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) { observer_->OnStopSyncingPermanently(); return; } +} - if (event.what_happened == SyncerEvent::UPDATED_TOKEN) { - observer_->OnUpdatedToken(event.updated_token); +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) 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( @@ -1897,8 +2121,8 @@ void SyncManager::SyncInternal::TalkMediatorLogin( << "(talk_mediator_ is NULL)"; return; } - LOG(INFO) << "P2P: Trying talk mediator login."; - + // TODO(akalin): Make talk_mediator automatically login on + // auth token change. talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME); talk_mediator_->Login(); } @@ -1960,7 +2184,7 @@ void SyncManager::SetupForTestMode(const std::wstring& test_username) { void SyncManager::SyncInternal::SetupForTestMode( const std::wstring& test_username) { - share_.name = WideToUTF8(test_username); + share_.authenticated_name = WideToUTF8(test_username); // Some tests are targeting only local db operations & integrity, and don't // want syncer thread interference. @@ -1968,8 +2192,18 @@ 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)); } } @@ -1979,7 +2213,7 @@ BaseTransaction::BaseTransaction(UserShare* share) : lookup_(NULL) { DCHECK(share && share->dir_manager.get()); lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(), - share->name); + share->authenticated_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 16f8bd9..e4e8510 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -107,14 +107,12 @@ struct UserShare { // be shared across multiple threads (unlike Directory). scoped_ptr<syncable::DirectoryManager> dir_manager; - // 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; + // 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; }; // A valid BaseNode will never have an ID of zero. @@ -671,9 +669,6 @@ 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; @@ -720,22 +715,52 @@ 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 SyncCredentials& credentials, + const char* lsid, bool use_chrome_async_socket, bool try_ssltcp_first, browser_sync::NotificationMethod notification_method, @@ -751,11 +776,15 @@ class SyncManager { // called. bool InitialSyncEndedForAllEnabledTypes(); - // 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); + // 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); // Start the SyncerThread. void StartSyncing(); diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc index b83c501..a77134b 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_.name = setter_upper_.name(); + share_.authenticated_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 2cc2300..17de218 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, - sessions::SyncSession* session, + AuthWatcher* auth_watcher, const ClientToServerMessage& msg, ClientToServerResponse* response) { @@ -144,9 +144,12 @@ bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm, std::string new_token = http_response.update_client_auth_header; if (!new_token.empty()) { - SyncerEvent event(SyncerEvent::UPDATED_TOKEN); - event.updated_token = new_token; - session->context()->syncer_event_channel()->Notify(event); + // 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); } if (response->ParseFromString(rx)) { @@ -186,7 +189,7 @@ bool SyncerProtoUtil::PostClientToServerMessage( } if (!PostAndProcessHeaders(session->context()->connection_manager(), - session, + session->context()->auth_watcher(), 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 ca68ec3..39fcc5c 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, - sessions::SyncSession* session, + browser_sync::AuthWatcher* authwatcher, 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 57cae0a..e6eb68b 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"), + : ServerConnectionManager("unused", 0, false, "version", "id"), 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 9f369ed8..800ddddb 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/syncer_thread.cc @@ -17,10 +17,12 @@ #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" @@ -73,12 +75,19 @@ 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()); @@ -89,6 +98,7 @@ 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()); @@ -577,18 +587,22 @@ void SyncerThread::HandleChannelEvent(const SyncerEvent& event) { NudgeSyncImpl(event.nudge_delay_milliseconds, kUnknown); } -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(); +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(); + } } // 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 158d759..8c71db4 100644 --- a/chrome/browser/sync/engine/syncer_thread.h +++ b/chrome/browser/sync/engine/syncer_thread.h @@ -29,6 +29,11 @@ class EventListenerHookup; +namespace syncable { +class DirectoryManager; +struct DirectoryManagerEvent; +} + namespace browser_sync { class ModelSafeWorker; @@ -133,9 +138,6 @@ 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); @@ -228,6 +230,8 @@ 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. @@ -319,6 +323,7 @@ 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 ded07f0..16328bd 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(), - metadb_.manager(), this); + NULL, 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); + SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, NULL); scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); } TEST_F(SyncerThreadTest, StartStop) { - SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL); + SyncSessionContext* context = new SyncSessionContext(NULL, 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); + SyncSessionContext* context = new SyncSessionContext(NULL, 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); + SyncSessionContext* context = new SyncSessionContext(NULL, 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,7 +673,6 @@ 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); @@ -698,7 +697,6 @@ 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); @@ -752,7 +750,6 @@ 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"); @@ -768,7 +765,6 @@ 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)); @@ -830,7 +826,6 @@ 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. @@ -874,7 +869,6 @@ 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. @@ -929,8 +923,6 @@ 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_)); @@ -1002,7 +994,6 @@ 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 0805c89..19be54e 100644 --- a/chrome/browser/sync/engine/syncer_types.h +++ b/chrome/browser/sync/engine/syncer_types.h @@ -78,8 +78,6 @@ 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, @@ -145,9 +143,6 @@ 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 74e5122..0d9ab8e 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(), + context_.reset(new SyncSessionContext(mock_server_.get(), NULL, 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 c4e0963..b570c4f 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -10,7 +10,6 @@ #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" @@ -22,25 +21,24 @@ #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, @@ -77,8 +75,10 @@ void SyncBackendHost::Initialize( const GURL& sync_service_url, const syncable::ModelTypeSet& types, URLRequestContextGetter* baseline_context_getter, - const SyncCredentials& credentials, + const std::string& lsid, 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,10 +110,13 @@ void SyncBackendHost::Initialize( } InitCore(Core::DoInitializeOptions( - sync_service_url, + sync_service_url, lsid.empty(), MakeHttpBridgeFactory(baseline_context_getter), - credentials, + MakeHttpBridgeFactory(baseline_context_getter), + lsid, delete_sync_data_folder, + invalidate_sync_login, + invalidate_sync_xmpp_login, use_chrome_async_socket, try_ssltcp_first, notification_method, @@ -145,11 +148,12 @@ void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) { options)); } -void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { +void SyncBackendHost::Authenticate(const std::string& username, + const std::string& password, + const std::string& captcha) { core_thread_.message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(core_.get(), - &SyncBackendHost::Core::DoUpdateCredentials, - credentials)); + NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoAuthenticate, + username, password, captcha)); } void SyncBackendHost::StartSyncingWithServer() { @@ -340,15 +344,6 @@ 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(); @@ -437,9 +432,8 @@ 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. @@ -451,11 +445,17 @@ 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.credentials, + options.lsid.c_str(), options.use_chrome_async_socket, options.try_ssltcp_first, options.notification_method, @@ -463,10 +463,11 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { DCHECK(success) << "Syncapi initialization failed!"; } -void SyncBackendHost::Core::DoUpdateCredentials( - const SyncCredentials& credentials) { +void SyncBackendHost::Core::DoAuthenticate(const std::string& username, + const std::string& password, + const std::string& captcha) { DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); - syncapi_->UpdateCredentials(credentials); + syncapi_->Authenticate(username.c_str(), password.c_str(), captcha.c_str()); } void SyncBackendHost::Core::DoStartSyncing() { @@ -623,7 +624,8 @@ bool SyncBackendHost::Core::IsCurrentThreadSafeForModel( void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) { - // Post to our core loop so we can modify state. Could be on another thread. + // We could be on SyncEngine_AuthWatcherThread. Post to our core loop so + // we can modify state. host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, auth_error)); @@ -658,11 +660,6 @@ 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 9e0a9f1..2b8680d 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -16,7 +16,6 @@ #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" @@ -102,14 +101,17 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void Initialize(const GURL& service_url, const syncable::ModelTypeSet& types, URLRequestContextGetter* baseline_context_getter, - const sync_api::SyncCredentials& credentials, + const std::string& lsid, 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 from |frontend_loop| to update SyncCredentials. - void UpdateCredentials(const sync_api::SyncCredentials& credentials); + // Called on |frontend_loop_| to kick off asynchronous authentication. + void Authenticate(const std::string& username, const std::string& password, + const std::string& captcha); // This starts the SyncerThread running a Syncer object to communicate with // sync servers. Until this is called, no changes will leave or enter this @@ -206,22 +208,29 @@ 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, - const sync_api::SyncCredentials& credentials, + sync_api::HttpPostProviderFactory* auth_http_bridge_factory, + const std::string& lsid, 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), - credentials(credentials), + auth_http_bridge_factory(auth_http_bridge_factory), + lsid(lsid), 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), @@ -230,7 +239,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { GURL service_url; bool attempt_last_user_authentication; sync_api::HttpPostProviderFactory* http_bridge_factory; - sync_api::SyncCredentials credentials; + sync_api::HttpPostProviderFactory* auth_http_bridge_factory; std::string lsid; bool delete_sync_data_folder; bool invalidate_sync_login; @@ -251,9 +260,11 @@ 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 credential - // update on behalf of SyncBackendHost::UpdateCredentials - void DoUpdateCredentials(const sync_api::SyncCredentials& credentials); + // 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 the SyncBackendHost core_thread_ to tell the syncapi to start // syncing (generally after initialization and authentication). @@ -297,17 +308,14 @@ 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) { - // 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); + DoInitialize(DoInitializeOptions(GURL(), false, factory, auth_factory, + std::string(), delete_sync_data_folder, + false, false, false, false, + notification_method, "")); + syncapi_->SetupForTestMode(test_user); } #endif @@ -350,9 +358,6 @@ 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( @@ -367,9 +372,6 @@ 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 a4a2550..1ca0473 100644 --- a/chrome/browser/sync/profile_sync_factory.h +++ b/chrome/browser/sync/profile_sync_factory.h @@ -6,9 +6,7 @@ #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" @@ -50,8 +48,7 @@ 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( - const std::string& cros_user) = 0; + virtual ProfileSyncService* CreateProfileSyncService() = 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 d6708cd..a5df306 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -76,11 +76,9 @@ ProfileSyncFactoryImpl::ProfileSyncFactoryImpl(Profile* profile, command_line_(command_line) { } -ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( - const std::string& cros_user) { - +ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService() { ProfileSyncService* pss = new ProfileSyncService( - this, profile_, cros_user); + this, profile_, browser_defaults::kBootstrapSyncAuthentication); // 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 233f3f1..ce535e5 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.h +++ b/chrome/browser/sync/profile_sync_factory_impl.h @@ -6,8 +6,6 @@ #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" @@ -20,8 +18,7 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory { virtual ~ProfileSyncFactoryImpl() {} // ProfileSyncFactory interface. - virtual ProfileSyncService* CreateProfileSyncService( - const std::string& cros_user); + virtual ProfileSyncService* CreateProfileSyncService(); 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 26f47d1..908ccae 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 18cc30c..6c14d1e 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_METHOD1(CreateProfileSyncService, - ProfileSyncService*(const std::string&)); + MOCK_METHOD0(CreateProfileSyncService, + ProfileSyncService*(void)); 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 567631d..5732ebf 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -30,10 +30,7 @@ #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" @@ -47,7 +44,6 @@ using browser_sync::ChangeProcessor; using browser_sync::DataTypeController; using browser_sync::DataTypeManager; using browser_sync::SyncBackendHost; -using sync_api::SyncCredentials; typedef GoogleServiceAuthError AuthError; @@ -59,20 +55,20 @@ const char* ProfileSyncService::kDevServerUrl = ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, Profile* profile, - const std::string& cros_user) + bool bootstrap_sync_authentication) : last_auth_error_(AuthError::None()), factory_(factory), profile_(profile), - cros_user_(cros_user), + bootstrap_sync_authentication_(bootstrap_sync_authentication), 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)), - token_migrator_(NULL) { + ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)) { DCHECK(factory); DCHECK(profile); registrar_.Add(this, @@ -113,8 +109,10 @@ 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), @@ -128,37 +126,6 @@ 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(); @@ -174,49 +141,24 @@ void ProfileSyncService::Initialize() { return; } - RegisterAuthNotifications(); - - // 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()) { + if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { 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. + + // 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(); } - } 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(); - } + StartUp(); } - -} - -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( @@ -290,7 +232,6 @@ 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. @@ -328,29 +269,23 @@ 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(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. + // TODO(akalin): Gather all the command-line-controlled switches + // into an Options struct to make passing them down less annoying. + 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 @@ -359,17 +294,15 @@ 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(), - credentials, + profile_->GetTokenService()->GetLsid(), delete_sync_data_folder, + invalidate_sync_login, + invalidate_sync_xmpp_login, use_chrome_async_socket_, try_ssltcp_first, notification_method_); @@ -388,9 +321,7 @@ void ProfileSyncService::StartUp() { return; } - DCHECK(AreCredentialsAvailable()); - - LOG(INFO) << "ProfileSyncService bringing up backend host."; + LOG(INFO) << "ProfileSyncSerivce bringing up backend host."; last_synced_time_ = base::Time::FromInternalValue( profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); @@ -424,21 +355,31 @@ 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) << "Disabling sync for user."; + LOG(INFO) << "Clearing Sync DB."; - // 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()); } @@ -500,20 +441,29 @@ 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." - << message; + LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."; std::string location; from_here.Write(true, true, &location); LOG(ERROR) << location; - // Shut all data types down. - MessageLoop::current()->PostTask(FROM_HERE, + 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, scoped_runnable_method_factory_.NewRunnableMethod( &ProfileSyncService::Shutdown, true)); + MessageLoop::current()->PostTask(FROM_HERE, + scoped_runnable_method_factory_.NewRunnableMethod( + &ProfileSyncService::StartUp)); + } } void ProfileSyncService::OnBackendInitialized() { @@ -522,12 +472,11 @@ 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 (!cros_user_.empty()) { + if (bootstrap_sync_authentication_) { if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { ShowChooseDataTypes(NULL); } else { @@ -535,9 +484,8 @@ void ProfileSyncService::OnBackendInitialized() { } } - if (HasSyncSetupCompleted()) { + if (HasSyncSetupCompleted()) ConfigureDataTypeManager(); - } } void ProfileSyncService::OnSyncCycleCompleted() { @@ -545,16 +493,22 @@ void ProfileSyncService::OnSyncCycleCompleted() { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } -void ProfileSyncService::UpdateAuthErrorState( - const GoogleServiceAuthError& error) { - last_auth_error_ = error; +void ProfileSyncService::OnAuthError() { + last_auth_error_ = backend_->GetAuthError(); // 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()) { + if (WizardIsVisible() || expecting_first_run_auth_needed_event_) { wizard_.Step(AuthError::NONE == last_auth_error_.state() ? SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN); - } else { + } + + if (expecting_first_run_auth_needed_event_) { + last_auth_error_ = AuthError::None(); + expecting_first_run_auth_needed_event_ = false; + } + + if (!WizardIsVisible()) { auth_error_time_ == base::TimeTicks::Now(); } @@ -569,10 +523,6 @@ void ProfileSyncService::UpdateAuthErrorState( 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); @@ -583,12 +533,6 @@ 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; @@ -600,10 +544,10 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { auth_error_time_ = base::TimeTicks(); // Reset auth_error_time_ to null. } - wizard_.SetParent(parent_window); - wizard_.Step(SyncSetupWizard::GAIA_LOGIN); - - FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); + if (last_auth_error_.state() != AuthError::NONE) { + wizard_.SetParent(parent_window); + wizard_.Step(SyncSetupWizard::GAIA_LOGIN); + } } void ProfileSyncService::ShowChooseDataTypes(gfx::NativeWindow parent_window) { @@ -679,26 +623,16 @@ 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(); - - // 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); + backend_->Authenticate(username, password, captcha); } void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, @@ -715,7 +649,7 @@ void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, } void ProfileSyncService::OnUserCancelledDialog() { - if (!HasSyncSetupCompleted()) { + if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { // A sync dialog was aborted before authentication. // Rollback. expect_sync_configuration_aborted_ = true; @@ -881,43 +815,10 @@ 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() && AreCredentialsAvailable()) { + else if (HasSyncSetupCompleted()) 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 efa2cb2..c0f023f 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -15,14 +15,12 @@ #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" @@ -36,7 +34,6 @@ 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 @@ -117,23 +114,13 @@ class ProfileSyncService : public browser_sync::SyncFrontend, ProfileSyncService(ProfileSyncFactory* factory_, Profile* profile, - const std::string& cros_user); + bool bootstrap_sync_authentication); 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 @@ -151,7 +138,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend, void GetDataTypeControllerStates( browser_sync::DataTypeController::StateMap* state_map) const; - // Disables sync for user. Use ShowLoginDialog to enable. + // Enables/disables sync for user. + virtual void EnableForUser(gfx::NativeWindow parent_window); virtual void DisableForUser(); // Whether sync is enabled by user or not. @@ -169,9 +157,6 @@ 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 @@ -341,9 +326,6 @@ 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(); @@ -352,8 +334,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend, return data_type_controllers_; } - // The wizard will try to read the auth state out of the profile sync - // service using this member. Captcha and error state are reflected. + // 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! GoogleServiceAuthError last_auth_error_; // Our asynchronous backend to communicate with sync components living on @@ -396,8 +380,11 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // The profile whose data we are synchronizing. Profile* profile_; - // Email for the ChromiumOS user, if we're running under ChromiumOS. - std::string cros_user_; + // 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_; // TODO(ncarter): Put this in a profile, once there is UI for it. // This specifies where to find the sync server. @@ -413,6 +400,17 @@ 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. @@ -421,9 +419,6 @@ 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. @@ -459,8 +454,6 @@ 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 d0884cc..e970457 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -31,7 +31,6 @@ #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" @@ -138,7 +137,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { void StartSyncService(Task* task, bool will_fail_association) { if (!service_.get()) { service_.reset( - new TestProfileSyncService(&factory_, &profile_, "test_user", false, + new TestProfileSyncService(&factory_, &profile_, false, false, task)); AutofillDataTypeController* data_type_controller = new AutofillDataTypeController(&factory_, @@ -165,13 +164,6 @@ 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 3142baf..1b36c63 100644 --- a/chrome/browser/sync/profile_sync_service_mock.h +++ b/chrome/browser/sync/profile_sync_service_mock.h @@ -20,6 +20,7 @@ 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 ea53bf7..c989822 100644 --- a/chrome/browser/sync/profile_sync_service_password_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc @@ -24,7 +24,6 @@ #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" @@ -141,7 +140,7 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { int num_pause_expectations) { if (!service_.get()) { service_.reset(new TestProfileSyncService(&factory_, &profile_, - "test_user", false, root_task)); + false, false, root_task)); service_->set_num_expected_resumes(num_resume_expectations); service_->set_num_expected_pauses(num_pause_expectations); PasswordDataTypeController* data_type_controller = @@ -156,13 +155,6 @@ 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 e1f3299..b706f7d 100644 --- a/chrome/browser/sync/profile_sync_service_preference_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_preference_unittest.cc @@ -19,7 +19,6 @@ #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" @@ -69,7 +68,7 @@ class ProfileSyncServicePreferenceTest return false; service_.reset(new TestProfileSyncService( - &factory_, profile_.get(), "test", false, task)); + &factory_, profile_.get(), false, false, task)); // Register the preference data type. model_associator_ = @@ -88,8 +87,6 @@ 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 23e4885..e12192b 100644 --- a/chrome/browser/sync/profile_sync_service_session_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc @@ -26,7 +26,6 @@ #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" @@ -111,7 +110,7 @@ class ProfileSyncServiceSessionTest return false; sync_service_.reset(new TestProfileSyncService( - &factory_, profile(), "test user", false, task)); + &factory_, profile(), false, false, task)); profile()->set_session_service(helper_.service()); // Register the session data type. @@ -127,8 +126,6 @@ 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 e831e36..d38e6c9 100644 --- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc @@ -15,7 +15,6 @@ #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" @@ -41,7 +40,6 @@ 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() @@ -56,7 +54,7 @@ class ProfileSyncServiceStartupTest : public testing::Test { virtual void SetUp() { service_.reset(new TestProfileSyncService(&factory_, &profile_, - "test", true, NULL)); + false, true, NULL)); service_->AddObserver(&observer_); service_->set_num_expected_resumes(0); service_->set_num_expected_pauses(0); @@ -101,17 +99,12 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) { Mock::VerifyAndClearExpectations(data_type_manager); // Then start things up. - EXPECT_CALL(*data_type_manager, Configure(_)).Times(2); + 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(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"); - + EXPECT_CALL(observer_, OnStateChanged()).Times(4); + service_->EnableForUser(NULL); syncable::ModelTypeSet set; set.insert(syncable::BOOKMARKS); service_->OnUserChoseDatatypes(false, set); @@ -126,9 +119,6 @@ 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(); } @@ -140,8 +130,6 @@ 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(); } @@ -150,8 +138,6 @@ 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. @@ -178,13 +164,51 @@ 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 f913433..17137ea4 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -27,7 +27,6 @@ #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" @@ -154,7 +153,7 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { void StartSyncService(Task* task) { if (!service_.get()) { service_.reset( - new TestProfileSyncService(&factory_, &profile_, "test", false, + new TestProfileSyncService(&factory_, &profile_, false, false, task)); TypedUrlDataTypeController* data_type_controller = new TypedUrlDataTypeController(&factory_, @@ -177,14 +176,7 @@ 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 8ea701a..bb366c7 100644 --- a/chrome/browser/sync/profile_sync_service_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_unittest.cc @@ -15,7 +15,6 @@ #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" @@ -33,7 +32,6 @@ #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" @@ -248,10 +246,9 @@ 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(), - "test", false, NULL)); + false, false, NULL)); if (!set_initial_sync_ended) service_->dont_set_initial_sync_ended_on_init(); @@ -270,9 +267,6 @@ 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(); } @@ -473,15 +467,15 @@ TEST_F(ProfileSyncServiceTest, InitialState) { TEST_F(ProfileSyncServiceTest, AbortedByShutdown) { service_.reset(new TestProfileSyncService(&factory_, profile_.get(), - "test", true, NULL)); + false, true, NULL)); service_->set_num_expected_resumes(0); - EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0); + EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). + WillOnce(ReturnNewDataTypeManager()); EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).Times(0); service_->RegisterDataTypeController( new browser_sync::BookmarkDataTypeController(&factory_, profile_.get(), service_.get())); - service_->Initialize(); service_.reset(); } @@ -1355,9 +1349,9 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) { if (!service_.get()) { service_.reset( new TestProfileSyncService(&factory_, profile_.get(), - "test", true, NULL)); + false, 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(), @@ -1382,25 +1376,26 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) { ASSERT_FALSE(service_->backend()); ASSERT_FALSE(service_->HasSyncSetupCompleted()); - // 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"); - + // This will actually start up the sync service. + service_->EnableForUser(NULL); syncable::ModelTypeSet set; set.insert(syncable::BOOKMARKS); service_->OnUserChoseDatatypes(false, set); - MessageLoop::current()->RunAllPending(); + MessageLoop::current()->Run(); // 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)); - // This will still exist, but the text should have changed. + // 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); + 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 4c6b63f..1e37a7c 100644 --- a/chrome/browser/sync/sessions/sync_session_context.h +++ b/chrome/browser/sync/sessions/sync_session_context.h @@ -31,6 +31,7 @@ class DirectoryManager; namespace browser_sync { +class AuthWatcher; class ConflictResolver; class ModelSafeWorkerRegistrar; class ServerConnectionManager; @@ -42,11 +43,13 @@ 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()), @@ -65,6 +68,9 @@ class SyncSessionContext { ServerConnectionManager* connection_manager() { return connection_manager_; } + AuthWatcher* auth_watcher() { + return auth_watcher_; + } syncable::DirectoryManager* directory_manager() { return directory_manager_; } @@ -119,6 +125,7 @@ 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 011f667..2bc4594 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, this)); + context_.reset(new SyncSessionContext(NULL, 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, db.manager(), this)); + context_.reset(new SyncSessionContext(NULL, 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 deleted file mode 100644 index 6f2a30c..0000000 --- a/chrome/browser/sync/signin_manager.cc +++ /dev/null @@ -1,95 +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/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 deleted file mode 100644 index a62514f..0000000 --- a/chrome/browser/sync/signin_manager.h +++ /dev/null @@ -1,81 +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. -// -// 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 deleted file mode 100644 index 208667c..0000000 --- a/chrome/browser/sync/signin_manager_unittest.cc +++ /dev/null @@ -1,93 +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/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 484d376..5ecd9b6 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, ""), + : ProfileSyncService(factory, profile, false), 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 32391ae..c7c1575 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->ShowLoginDialog(NULL); - ProfileSyncService::SyncEvent(code); // UMA stats + service->EnableForUser(NULL); + ProfileSyncService::SyncEvent(code); } } diff --git a/chrome/browser/sync/syncable/directory_manager.cc b/chrome/browser/sync/syncable/directory_manager.cc index fbdeb46..0719c67 100644 --- a/chrome/browser/sync/syncable/directory_manager.cc +++ b/chrome/browser/sync/syncable/directory_manager.cc @@ -54,6 +54,17 @@ 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 0c0b55d..a6f0847 100644 --- a/chrome/browser/sync/syncable/directory_manager.h +++ b/chrome/browser/sync/syncable/directory_manager.h @@ -31,11 +31,14 @@ 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 0854a6b..aba7c42 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->name); + ScopedDirLookup dir(dir_manager, user_share->authenticated_name); if (!dir.good()) FAIL(); @@ -154,6 +154,7 @@ 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)); @@ -185,12 +186,10 @@ class TestProfileSyncService : public ProfileSyncService { public: TestProfileSyncService(ProfileSyncFactory* factory, Profile* profile, - const std::string& test_user, + bool bootstrap_sync_authentication, bool synchronous_backend_initialization, Task* initial_condition_setup_task) - : ProfileSyncService(factory, profile, - !test_user.empty() ? - test_user : ""), + : ProfileSyncService(factory, profile, bootstrap_sync_authentication), 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 deleted file mode 100644 index 084bc40..0000000 --- a/chrome/browser/sync/token_migrator.cc +++ /dev/null @@ -1,53 +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/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 deleted file mode 100644 index 6ee3394..0000000 --- a/chrome/browser/sync/token_migrator.h +++ /dev/null @@ -1,53 +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 <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 9af444f..41b9ffa 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_->ShowLoginDialog(GetWindow()->GetNativeWindow()); + sync_service_->EnableForUser(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 47e84f4..8851192 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -903,6 +903,10 @@ '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 e6343f3..257b1e0 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.cc', 'browser/sync/glue/autofill_change_processor.h', + 'browser/sync/glue/autofill_change_processor.cc', 'browser/sync/glue/autofill_data_type_controller.cc', 'browser/sync/glue/autofill_data_type_controller.h', - 'browser/sync/glue/autofill_model_associator.cc', 'browser/sync/glue/autofill_model_associator.h', + 'browser/sync/glue/autofill_model_associator.cc', '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.cc', 'browser/sync/glue/bookmark_model_associator.h', + 'browser/sync/glue/bookmark_model_associator.cc', 'browser/sync/glue/change_processor.cc', 'browser/sync/glue/change_processor.h', 'browser/sync/glue/data_type_controller.h', @@ -2566,14 +2566,16 @@ '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.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_sync.cc', + 'browser/sync/glue/extension_sync.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', @@ -2583,8 +2585,6 @@ '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,15 +2616,13 @@ '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.cc', 'browser/sync/notification_method.h', + 'browser/sync/notification_method.cc', + 'browser/sync/profile_sync_service.cc', + 'browser/sync/profile_sync_service.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', @@ -2633,10 +2631,8 @@ 'browser/sync/sync_setup_wizard.h', 'browser/sync/sync_ui_util.cc', 'browser/sync/sync_ui_util.h', - '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/sync/sync_ui_util_mac.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 f0470e9..d9c244c 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1147,7 +1147,6 @@ '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', @@ -1264,7 +1263,6 @@ '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', @@ -2180,6 +2178,7 @@ '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', @@ -2354,6 +2353,7 @@ '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 834b9e5..df520c6 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -1045,13 +1045,6 @@ 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 @@ -1060,32 +1053,16 @@ class NotificationType { // TokenAvailableDetails object. TOKEN_AVAILABLE, - // 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, + // Sent when the sidebar state is changed. + // The source is a SidebarManager instance, the details are the changed + // SidebarContainer object. + SIDEBAR_CHANGED, // 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 168f4e8..2cd8b84 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -923,17 +923,10 @@ 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 1caaa31b..6321c6a 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -342,8 +342,6 @@ 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 9fc8598..5ff3e74 100644 --- a/chrome/test/live_sync/live_sync_test.cc +++ b/chrome/test/live_sync/live_sync_test.cc @@ -22,7 +22,6 @@ #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" @@ -232,10 +231,6 @@ 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)); } @@ -260,12 +255,10 @@ 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) { @@ -274,7 +267,6 @@ 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 new file mode 100644 index 0000000..c64df34 --- /dev/null +++ b/chrome/test/live_sync/offline_sync_test.cc @@ -0,0 +1,29 @@ +// 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 b49bf56..12448da 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.cc +++ b/chrome/test/live_sync/profile_sync_service_test_harness.cc @@ -7,11 +7,9 @@ #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" @@ -86,8 +84,7 @@ bool StateChangeTimeoutEvent::Abort() { ProfileSyncServiceTestHarness::ProfileSyncServiceTestHarness(Profile* p, const std::string& username, const std::string& password, int id) - : wait_state_(WAITING_FOR_ON_BACKEND_INITIALIZED), - profile_(p), service_(NULL), + : wait_state_(WAITING_FOR_ON_AUTH_ERROR), profile_(p), service_(NULL), last_status_(kInvalidStatus), last_timestamp_(0), min_timestamp_needed_(kMinTimestampNeededNone), @@ -98,11 +95,15 @@ ProfileSyncServiceTestHarness::ProfileSyncServiceTestHarness(Profile* p, } bool ProfileSyncServiceTestHarness::SetupSync() { - service_ = profile_->GetProfileSyncService(""); + service_ = profile_->GetProfileSyncService(); + service_->StartUp(); service_->AddObserver(this); - service_->signin_.StartSignIn(username_, password_, "", ""); + return WaitForServiceInit(false); +} - return WaitForServiceInit(); +bool ProfileSyncServiceTestHarness::RetryAuthentication() { + wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED; + return WaitForServiceInit(true); } void ProfileSyncServiceTestHarness::SignalStateCompleteWithNextState( @@ -120,6 +121,11 @@ 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) { @@ -137,21 +143,6 @@ 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(); @@ -160,10 +151,7 @@ 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 (service()->backend()->HasUnsyncedItems() || - snap->has_more_to_sync || snap->unsynced_count != 0) { - if (!status.server_reachable) - SignalStateCompleteWithNextState(WAITING_FOR_SERVER_REACHABLE); + if (snap->has_more_to_sync || snap->unsynced_count != 0) { break; } EXPECT_LE(last_timestamp_, snap->max_local_timestamp); @@ -298,10 +286,18 @@ bool ProfileSyncServiceTestHarness::AwaitStatusChangeWithTimeout( return timeout_signal->Abort(); } -bool ProfileSyncServiceTestHarness::WaitForServiceInit() { +bool ProfileSyncServiceTestHarness::WaitForServiceInit(bool is_auth_retry) { 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."; + } - // Wait for the OnBackendInitialized() callback. + // Enter GAIA credentials and wait for the OnBackendInitialized() callback. + service_->backend()->Authenticate(username_, password_, std::string()); EXPECT_EQ(wait_state_, WAITING_FOR_ON_BACKEND_INITIALIZED); EXPECT_TRUE(AwaitStatusChangeWithTimeout(30, "Waiting for OnBackendInitialized().")) << @@ -311,13 +307,15 @@ bool ProfileSyncServiceTestHarness::WaitForServiceInit() { return false; } - // 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)); + // 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); } - service_->OnUserChoseDatatypes(true, set); // Wait for notifications_enabled to be set to true. EXPECT_EQ(wait_state_, WAITING_FOR_NOTIFICATIONS_ENABLED); @@ -331,10 +329,7 @@ bool ProfileSyncServiceTestHarness::WaitForServiceInit() { const SyncSessionSnapshot* ProfileSyncServiceTestHarness::GetLastSessionSnapshot() const { EXPECT_FALSE(service_ == NULL) << "Sync service has not yet been set up."; - if (service_->backend()) { - return service_->backend()->GetLastSessionSnapshot(); - } - return NULL; + return service_->backend()->GetLastSessionSnapshot(); } 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 34cfde3..3b32930 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.h +++ b/chrome/test/live_sync/profile_sync_service_test_harness.h @@ -31,6 +31,10 @@ 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(); @@ -75,16 +79,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 = 0, + WAITING_FOR_ON_BACKEND_INITIALIZED, // 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. @@ -106,7 +110,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 WaitForServiceInit(bool is_auth_retry); // 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 bc66917..c4cc585 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,37 +4,6 @@ #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 b3b0dcf..67f7ee2 100644 --- a/chrome/test/profile_mock.h +++ b/chrome/test/profile_mock.h @@ -18,7 +18,6 @@ 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 24ad1f2..bfba668 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"), + : ServerConnectionManager("unused", 0, false, "version", "id"), 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 02ea57d..eed8def 100644 --- a/chrome/test/sync/engine/syncer_command_test.h +++ b/chrome/test/sync/engine/syncer_command_test.h @@ -48,9 +48,6 @@ 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) { @@ -94,7 +91,7 @@ class SyncerCommandTestWithParam : public testing::TestWithParam<T>, void ResetContext() { context_.reset(new sessions::SyncSessionContext( - mock_server_.get(), syncdb_->manager(), registrar())); + mock_server_.get(), NULL, 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 6fd04fc..c6ad9b3 100644 --- a/chrome/test/testing_profile.cc +++ b/chrome/test/testing_profile.cc @@ -453,11 +453,6 @@ 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 d75b7c1..6399692 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -224,7 +224,6 @@ class TestingProfile : public Profile { virtual void InitExtensions() {} virtual void InitWebResources() {} virtual NTPResourceCache* GetNTPResourceCache(); - virtual DesktopNotificationService* GetDesktopNotificationService(); virtual BackgroundContentsService* GetBackgroundContentsService() { return NULL; @@ -253,8 +252,6 @@ 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() { |