diff options
author | rsimha@chromium.org <rsimha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-13 22:00:13 +0000 |
---|---|---|
committer | rsimha@chromium.org <rsimha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-13 22:00:13 +0000 |
commit | 88dde5aedfe775435c6a4f11a7ca2555f86cfe16 (patch) | |
tree | 8c4437828334dc52bb49d89a4510a31adb4baddb | |
parent | 6145d405223b95d9c2054581e95b66979fe6f15a (diff) | |
download | chromium_src-88dde5aedfe775435c6a4f11a7ca2555f86cfe16.zip chromium_src-88dde5aedfe775435c6a4f11a7ca2555f86cfe16.tar.gz chromium_src-88dde5aedfe775435c6a4f11a7ca2555f86cfe16.tar.bz2 |
[sync] Auto-create credential cache for users who are already signed in and go on to upgrade Chrome
CredentialCacheService on Windows 8 writes to the local credential cache
when a user actively signs in / reconfigures sync. Existing cached
credentials are not updated when Chrome is merely restarted after the
user is signed in.
This causes a problem when a user is already signed in, and then
upgrades (and restarts) chrome from a version that didn't originally
support credential caching. In such cases, we never end up caching
credentials, and therefore, the user will have to sign in separately to
Metro and Desktop.
This patch contains the following changes:
1) Adds logic to auto-heal already-signed-in users who upgrade from
older versions, by writing existing credentials to the local cache if
during restart, we notice that there is no local cache file, and the
user is already signed in to sync.
2) Simplifies the logic around checking if an alternate credential cache
file exists, and only then initializing |alternate_store_|. It turns out
that JsonPrefStore returns a useful PrefReadError field, and there is no
need for CCS to do funky stuff on the FILE thread.
3) Simplifies OnInitialzationCompleted, which was being used to observe
two separate JsonPrefStores. Instead of having CCS be a
PrefStore::Observer, we now use two helper classes -- LocalStoreObserver
and AlternateStoreObserver to cleanly divide what is done when each pref
store is initialized.
4) Updates prefs::kGoogleServicesUsername by listening to the notifications
NOTIFICATION_GOOGLE_SIGNED_OUT and NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
instead of directly listening to the pref change.
5) Fixes a stray instance where we were accessing the gaia username
pref via SyncPrefs instead of via the SigninManager.
BUG=141555
TEST=Sign in to chrome, exit the browser, and delete "Sync Credentials" from the default profile directory. Restart Chrome and make sure that the credential cache file is freshly written using existing sync credentials.
Review URL: https://chromiumcodereview.appspot.com/10830239
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151364 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/signin/token_service.h | 4 | ||||
-rw-r--r-- | chrome/browser/sync/credential_cache_service_win.cc | 475 | ||||
-rw-r--r-- | chrome/browser/sync/credential_cache_service_win.h | 91 | ||||
-rw-r--r-- | chrome/browser/sync/credential_cache_service_win_unittest.cc | 3 |
4 files changed, 337 insertions, 236 deletions
diff --git a/chrome/browser/signin/token_service.h b/chrome/browser/signin/token_service.h index ea7bd6e..084b753 100644 --- a/chrome/browser/signin/token_service.h +++ b/chrome/browser/signin/token_service.h @@ -177,6 +177,10 @@ class TokenService : public GaiaAuthConsumer, void IssueAuthTokenForTest(const std::string& service, const std::string& auth_token); + const GaiaAuthConsumer::ClientLoginResult& credentials() const { + return credentials_; + } + // GaiaAuthConsumer implementation. virtual void OnIssueAuthTokenSuccess(const std::string& service, const std::string& auth_token) OVERRIDE; diff --git a/chrome/browser/sync/credential_cache_service_win.cc b/chrome/browser/sync/credential_cache_service_win.cc index 6dc7eee..85cd54c 100644 --- a/chrome/browser/sync/credential_cache_service_win.cc +++ b/chrome/browser/sync/credential_cache_service_win.cc @@ -17,6 +17,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/signin/signin_manager.h" +#include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/signin/token_service.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/sync/glue/chrome_encryptor.h" @@ -55,7 +56,11 @@ CredentialCacheService::CredentialCacheService(Profile* profile) weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { if (profile_) { InitializeLocalCredentialCacheWriter(); - if (ShouldLookForCachedCredentialsInAlternateProfile()) + // If sync is not disabled, look for credentials in the alternate profile. + // Note that we do want to look for credentials in the alternate profile + // even if the local user is signed in, so that we can detect a sign out or + // reconfigure originating from the alternate profile. + if (!sync_prefs_.IsManaged()) LookForCachedCredentialsInAlternateProfile(); } } @@ -65,32 +70,10 @@ CredentialCacheService::~CredentialCacheService() { } void CredentialCacheService::Shutdown() { - if (local_store_.get()) { - local_store_.release(); - } - - if (alternate_store_.get()) { - alternate_store_->RemoveObserver(this); - alternate_store_.release(); - } -} - -void CredentialCacheService::OnInitializationCompleted(bool succeeded) { - DCHECK(succeeded); - // When the local and alternate credential stores become available, begin - // consuming the alternate cached credentials. We must also wait for the local - // credential store because the credentials read from the alternate cache and - // applied locally must eventually get stored in the local cache. - if (alternate_store_.get() && - alternate_store_->IsInitializationComplete() && - local_store_.get() && - local_store_->IsInitializationComplete()) { - ReadCachedCredentialsFromAlternateProfile(); - } -} - -void CredentialCacheService::OnPrefValueChanged(const std::string& key) { - // Nothing to do here, since credentials are cached silently. + local_store_observer_.release(); + local_store_.release(); + alternate_store_observer_.release(); + alternate_store_.release(); } void CredentialCacheService::Observe( @@ -103,11 +86,9 @@ void CredentialCacheService::Observe( case chrome::NOTIFICATION_PREF_CHANGED: { const std::string pref_name = *(content::Details<const std::string>(details).ptr()); - if (pref_name == prefs::kGoogleServicesUsername || - pref_name == prefs::kSyncEncryptionBootstrapToken) { - PackAndUpdateStringPref( - pref_name, - profile_->GetPrefs()->GetString(pref_name.c_str())); + if (pref_name == prefs::kSyncEncryptionBootstrapToken) { + PackAndUpdateStringPref(pref_name, + sync_prefs_.GetEncryptionBootstrapToken()); } else { UpdateBooleanPref(pref_name, profile_->GetPrefs()->GetBoolean(pref_name.c_str())); @@ -115,6 +96,36 @@ void CredentialCacheService::Observe( break; } + case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: + case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: { + SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); + PackAndUpdateStringPref(prefs::kGoogleServicesUsername, + signin->GetAuthenticatedUsername()); + break; + } + + case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { + // If there is no existing local credential cache, and the token service + // already has valid credentials as a result of the user having signed in, + // write them to the cache. Used in cases where the user was already + // signed in and then upgraded from a version of chrome that didn't + // support credential caching. + if (local_store_.get() && + local_store_->IsInitializationComplete() && + local_store_->GetReadError() == + JsonPrefStore::PREF_READ_ERROR_NO_FILE) { + TokenService* token_service = + TokenServiceFactory::GetForProfile(profile_); + if (token_service->AreCredentialsValid()) { + GaiaAuthConsumer::ClientLoginResult credentials = + token_service->credentials(); + PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, credentials.lsid); + PackAndUpdateStringPref(GaiaConstants::kGaiaSid, credentials.sid); + } + } + break; + } + case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { const TokenService::CredentialsUpdatedDetails& token_details = *(content::Details<const TokenService::CredentialsUpdatedDetails>( @@ -137,6 +148,152 @@ void CredentialCacheService::Observe( } } +void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() { + // If the local user has signed in and signed out, we do not consume cached + // credentials from the alternate profile. There is nothing more to do, now or + // later on. + if (HasUserSignedOut()) + return; + + // Sanity check the alternate credential cache. If any string credentials + // are outright missing even though the file exists, something is awry with + // the alternate profile store. There is no sense in flagging an error as the + // problem lies in a different profile directory. There is nothing to do now. + // We schedule a future read from the alternate credential cache and return. + DCHECK(alternate_store_.get()); + if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) || + !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) || + !HasPref(alternate_store_, GaiaConstants::kGaiaSid) || + !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) || + !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { + VLOG(1) << "Could not find cached credentials in \"" + << GetCredentialPathInAlternateProfile().value() << "\"."; + ScheduleNextReadFromAlternateCredentialCache(); + return; + } + + // Extract cached credentials from the alternate credential cache. + std::string google_services_username = + GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); + std::string lsid = + GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); + std::string sid = + GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); + std::string encryption_bootstrap_token = + GetAndUnpackStringPref(alternate_store_, + prefs::kSyncEncryptionBootstrapToken); + + // Sign out of sync if the alternate profile has signed out the same user. + // There is no need to schedule any more reads of the alternate profile + // cache because we only apply cached credentials for first-time sign-ins. + if (ShouldSignOutOfSync(google_services_username)) { + VLOG(1) << "User has signed out on the other profile. Signing out."; + InitiateSignOut(); + return; + } + + // Extract cached sync prefs from the alternate credential cache. + bool keep_everything_synced = + GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); + ProfileSyncService* service = + ProfileSyncServiceFactory::GetForProfile(profile_); + ModelTypeSet registered_types = service->GetRegisteredDataTypes(); + ModelTypeSet preferred_types; + for (ModelTypeSet::Iterator it = registered_types.First(); + it.Good(); + it.Inc()) { + std::string datatype_pref_name = + browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); + if (!HasPref(alternate_store_, datatype_pref_name)) { + // If there is no cached pref for a specific data type, it means that the + // user originally signed in with an older version of Chrome, and then + // upgraded to a version with a new datatype. In such cases, we leave the + // default initial datatype setting as false while reading cached + // credentials, just like we do in SyncPrefs::RegisterPreferences. + VLOG(1) << "Could not find cached datatype pref for " + << datatype_pref_name << " in " + << GetCredentialPathInAlternateProfile().value() << "."; + continue; + } + if (GetBooleanPref(alternate_store_, datatype_pref_name)) + preferred_types.Put(it.Get()); + } + + // Reconfigure if sync settings or credentials have changed in the alternate + // profile, but for the same user that is signed in to the local profile. + if (MayReconfigureSync(google_services_username)) { + if (HaveSyncPrefsChanged(keep_everything_synced, preferred_types)) { + VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; + service->OnUserChoseDatatypes(keep_everything_synced, preferred_types); + } + if (HaveTokenServiceCredentialsChanged(lsid, sid)) { + VLOG(1) << "Token service credentials have changed in other profile."; + UpdateTokenServiceCredentials(lsid, sid); + } + } + + // Sign in if we notice new cached credentials in the alternate profile. + if (ShouldSignInToSync(google_services_username, + lsid, + sid, + encryption_bootstrap_token)) { + InitiateSignInWithCachedCredentials(google_services_username, + encryption_bootstrap_token, + keep_everything_synced, + preferred_types); + UpdateTokenServiceCredentials(lsid, sid); + } + + // Schedule the next read from the alternate credential cache so that we can + // detect future reconfigures or sign outs. + ScheduleNextReadFromAlternateCredentialCache(); +} + +void CredentialCacheService::WriteExistingSyncPrefsToLocalCache() { + // If the local user is already signed in and there is no local credential + // cache file, write all the existing sync prefs to the local cache. + DCHECK(local_store_.get() && + local_store_->GetReadError() == + JsonPrefStore::PREF_READ_ERROR_NO_FILE); + SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); + if (!signin->GetAuthenticatedUsername().empty() && + !HasPref(local_store_, prefs::kGoogleServicesUsername)) { + PackAndUpdateStringPref(prefs::kGoogleServicesUsername, + signin->GetAuthenticatedUsername()); + PackAndUpdateStringPref(prefs::kSyncEncryptionBootstrapToken, + sync_prefs_.GetEncryptionBootstrapToken()); + UpdateBooleanPref(prefs::kSyncKeepEverythingSynced, + sync_prefs_.HasKeepEverythingSynced()); + ProfileSyncService* service = + ProfileSyncServiceFactory::GetForProfile(profile_); + ModelTypeSet registered_types = service->GetRegisteredDataTypes(); + for (ModelTypeSet::Iterator it = registered_types.First(); + it.Good(); + it.Inc()) { + std::string datatype_pref_name = + browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); + UpdateBooleanPref( + datatype_pref_name, + profile_->GetPrefs()->GetBoolean(datatype_pref_name.c_str())); + } + } +} + +void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache() { + // We must reinitialize |alternate_store_| here because the underlying + // credential file in the alternate profile might have changed, and we must + // re-read it afresh. + alternate_store_observer_.release(); + alternate_store_.release(); + next_read_.Reset(base::Bind( + &CredentialCacheService::LookForCachedCredentialsInAlternateProfile, + weak_factory_.GetWeakPtr())); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + next_read_.callback(), + TimeDelta::FromSeconds(kCredentialCachePollIntervalSecs)); +} + bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, const std::string& pref_name) { return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); @@ -263,6 +420,65 @@ bool CredentialCacheService::GetBooleanPref( return pref; } +CredentialCacheService::LocalStoreObserver::LocalStoreObserver( + CredentialCacheService* service, + scoped_refptr<JsonPrefStore> local_store) + : service_(service), + local_store_(local_store) { + local_store_->AddObserver(this); +} + +CredentialCacheService::LocalStoreObserver::~LocalStoreObserver() { + local_store_->RemoveObserver(this); +} + +void CredentialCacheService::LocalStoreObserver::OnInitializationCompleted( + bool succeeded) { + // If there is no existing local credential cache, write any existing sync + // prefs to the local cache. This could happen if the user was already signed + // in and restarts chrome after upgrading from an older version that didn't + // support credential caching. Note that |succeeded| will be true even if + // the local cache file wasn't found, so long as its parent dir was found. + DCHECK(succeeded); + if (local_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NO_FILE) { + service_->WriteExistingSyncPrefsToLocalCache(); + } +} + +void CredentialCacheService::LocalStoreObserver::OnPrefValueChanged( + const std::string& key) { + // Nothing to do here, since credentials are cached silently. +} + +CredentialCacheService::AlternateStoreObserver::AlternateStoreObserver( + CredentialCacheService* service, + scoped_refptr<JsonPrefStore> alternate_store) + : service_(service), + alternate_store_(alternate_store) { + alternate_store_->AddObserver(this); +} + +CredentialCacheService::AlternateStoreObserver::~AlternateStoreObserver() { + alternate_store_->RemoveObserver(this); +} + +void CredentialCacheService::AlternateStoreObserver::OnInitializationCompleted( + bool succeeded) { + // If an alternate credential cache was found, begin consuming its contents. + // If not, schedule a future read. + if (succeeded && + alternate_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE) { + service_->ReadCachedCredentialsFromAlternateProfile(); + } else { + service_->ScheduleNextReadFromAlternateCredentialCache(); + } +} + +void CredentialCacheService::AlternateStoreObserver::OnPrefValueChanged( + const std::string& key) { + // Nothing to do here, since credentials are cached silently. +} + FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { // The sync credential path in the default Desktop profile is // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while @@ -281,31 +497,26 @@ FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); } -bool CredentialCacheService::ShouldLookForCachedCredentialsInAlternateProfile() - const { - // We must look for credentials in the alternate profile iff the following are - // true: - // 1) Sync is not disabled by policy. - // 2) Sync startup is not suppressed. - // Note that we do want to look for credentials in the alternate profile even - // if the local user is signed in, so we can detect a sign out originating - // from the alternate profile. - return !sync_prefs_.IsManaged() && !sync_prefs_.IsStartSuppressed(); -} - void CredentialCacheService::InitializeLocalCredentialCacheWriter() { local_store_ = new JsonPrefStore( GetCredentialPathInCurrentProfile(), content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::FILE)); - local_store_->AddObserver(this); + local_store_observer_ = new LocalStoreObserver(this, local_store_); local_store_->ReadPrefsAsync(NULL); - // Register for notifications for updates to the sync credentials, which are + // Register for notifications for google sign in and sign out. + registrar_.Add(this, + chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, + content::Source<Profile>(profile_)); + registrar_.Add(this, + chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, + content::Source<Profile>(profile_)); + + // Register for notifications for updates to various sync settings, which are // stored in the PrefStore. pref_registrar_.Init(profile_->GetPrefs()); pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); - pref_registrar_.Add(prefs::kGoogleServicesUsername, this); pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); ModelTypeSet all_types = syncer::ModelTypeSet::All(); for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { @@ -320,6 +531,9 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() { // the TokenService. TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); registrar_.Add(this, + chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, + content::Source<TokenService>(token_service)); + registrar_.Add(this, chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, content::Source<TokenService>(token_service)); registrar_.Add(this, @@ -327,24 +541,16 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() { content::Source<TokenService>(token_service)); } -void CredentialCacheService::InitializeAlternateCredentialCacheReader( - bool* should_initialize) { - // If |should_initialize| is false, there was no credential cache in the - // alternate profile directory, and there is nothing to do right now. Schedule - // another read in the future and exit. - DCHECK(should_initialize); - if (!*should_initialize) { - ScheduleNextReadFromAlternateCredentialCache(); - return; - } - - // A credential cache file was found in the alternate profile. Prepare to - // consume its contents. +void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { + // Attempt to read cached credentials from the alternate profile. If no file + // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned + // after initialization is complete. alternate_store_ = new JsonPrefStore( GetCredentialPathInAlternateProfile(), content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::FILE)); - alternate_store_->AddObserver(this); + alternate_store_observer_ = new AlternateStoreObserver(this, + alternate_store_); alternate_store_->ReadPrefsAsync(NULL); } @@ -358,136 +564,6 @@ bool CredentialCacheService::HasUserSignedOut() { prefs::kGoogleServicesUsername).empty(); } -namespace { - -// Determines if there is a sync credential cache in the alternate profile. -// Returns true via |result| if there is a credential cache file in the -// alternate profile. Returns false otherwise. -void AlternateCredentialCacheExists( - const FilePath& credential_path_in_alternate_profile, - bool* result) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); - DCHECK(result); - *result = file_util::PathExists(credential_path_in_alternate_profile); -} - -} // namespace - -void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { - bool* should_initialize = new bool(false); - content::BrowserThread::PostTaskAndReply( - content::BrowserThread::FILE, - FROM_HERE, - base::Bind(&AlternateCredentialCacheExists, - GetCredentialPathInAlternateProfile(), - should_initialize), - base::Bind( - &CredentialCacheService::InitializeAlternateCredentialCacheReader, - weak_factory_.GetWeakPtr(), - base::Owned(should_initialize))); -} - -void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() { - // If the local user has signed in and signed out, we do not consume cached - // credentials from the alternate profile. There is nothing more to do, now or - // later on. - if (HasUserSignedOut()) - return; - - // Sanity check the alternate credential cache. If any string credentials - // are outright missing even though the file exists, something is awry with - // the alternate profile store. There is no sense in flagging an error as the - // problem lies in a different profile directory. There is nothing to do now. - // We schedule a future read from the alternate credential cache and return. - DCHECK(alternate_store_.get()); - if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) || - !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) || - !HasPref(alternate_store_, GaiaConstants::kGaiaSid) || - !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) || - !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { - VLOG(1) << "Could not find cached credentials in \"" - << GetCredentialPathInAlternateProfile().value() << "\"."; - ScheduleNextReadFromAlternateCredentialCache(); - return; - } - - // Extract cached credentials from the alternate credential cache. - std::string google_services_username = - GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); - std::string lsid = - GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); - std::string sid = - GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); - std::string encryption_bootstrap_token = - GetAndUnpackStringPref(alternate_store_, - prefs::kSyncEncryptionBootstrapToken); - - // Sign out of sync if the alternate profile has signed out the same user. - // There is no need to schedule any more reads of the alternate profile - // cache because we only apply cached credentials for first-time sign-ins. - if (ShouldSignOutOfSync(google_services_username)) { - VLOG(1) << "User has signed out on the other profile. Signing out."; - InitiateSignOut(); - return; - } - - // Extract cached sync prefs from the alternate credential cache. - bool keep_everything_synced = - GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); - ProfileSyncService* service = - ProfileSyncServiceFactory::GetForProfile(profile_); - ModelTypeSet registered_types = service->GetRegisteredDataTypes(); - ModelTypeSet preferred_types; - for (ModelTypeSet::Iterator it = registered_types.First(); - it.Good(); - it.Inc()) { - std::string datatype_pref_name = - browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); - if (!HasPref(alternate_store_, datatype_pref_name)) { - // If there is no cached pref for a specific data type, it means that the - // user originally signed in with an older version of Chrome, and then - // upgraded to a version with a new datatype. In such cases, we leave the - // default initial datatype setting as false while reading cached - // credentials, just like we do in SyncPrefs::RegisterPreferences. - VLOG(1) << "Could not find cached datatype pref for " - << datatype_pref_name << " in " - << GetCredentialPathInAlternateProfile().value() << "."; - continue; - } - if (GetBooleanPref(alternate_store_, datatype_pref_name)) - preferred_types.Put(it.Get()); - } - - // Reconfigure if sync settings or credentials have changed in the alternate - // profile, but for the same user that is signed in to the local profile. - if (MayReconfigureSync(google_services_username)) { - if (HaveSyncPrefsChanged(keep_everything_synced, preferred_types)) { - VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; - service->OnUserChoseDatatypes(keep_everything_synced, preferred_types); - } - if (HaveTokenServiceCredentialsChanged(lsid, sid)) { - VLOG(1) << "Token service credentials have changed in other profile."; - UpdateTokenServiceCredentials(lsid, sid); - } - } - - // Sign in if we notice new cached credentials in the alternate profile. - if (ShouldSignInToSync(google_services_username, - lsid, - sid, - encryption_bootstrap_token)) { - InitiateSignInWithCachedCredentials(google_services_username, - encryption_bootstrap_token, - keep_everything_synced, - preferred_types); - UpdateTokenServiceCredentials(lsid, sid); - } - - // Schedule the next read from the alternate credential cache so that we can - // detect future reconfigures or sign outs. - ScheduleNextReadFromAlternateCredentialCache(); -} - void CredentialCacheService::InitiateSignInWithCachedCredentials( const std::string& google_services_username, const std::string& encryption_bootstrap_token, @@ -607,21 +683,4 @@ bool CredentialCacheService::ShouldSignInToSync( !service->setup_in_progress(); } -void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache() { - // We must reinitialize |alternate_store_| here because the underlying - // credential file in the alternate profile might have changed, and we must - // re-read it afresh. - if (alternate_store_.get()) { - alternate_store_->RemoveObserver(this); - alternate_store_.release(); - } - next_read_.Reset(base::Bind( - &CredentialCacheService::LookForCachedCredentialsInAlternateProfile, - weak_factory_.GetWeakPtr())); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - next_read_.callback(), - TimeDelta::FromSeconds(kCredentialCachePollIntervalSecs)); -} - } // namespace syncer diff --git a/chrome/browser/sync/credential_cache_service_win.h b/chrome/browser/sync/credential_cache_service_win.h index 4543d01..f460a93 100644 --- a/chrome/browser/sync/credential_cache_service_win.h +++ b/chrome/browser/sync/credential_cache_service_win.h @@ -39,8 +39,7 @@ namespace syncer { // sync using credentials that were cached due to signing in on the other // (alternate) mode. class CredentialCacheService : public ProfileKeyedService, - public content::NotificationObserver, - public PrefStore::Observer { + public content::NotificationObserver { public: explicit CredentialCacheService(Profile* profile); virtual ~CredentialCacheService(); @@ -48,15 +47,26 @@ class CredentialCacheService : public ProfileKeyedService, // ProfileKeyedService implementation. virtual void Shutdown() OVERRIDE; - // PrefStore::Observer implementation. - virtual void OnInitializationCompleted(bool succeeded) OVERRIDE; - virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; - // content::NotificationObserver implementation. virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; + // Loads cached sync credentials from the alternate profile and applies them + // to the local profile if the load was successful. + void ReadCachedCredentialsFromAlternateProfile(); + + // Populates a new local credential cache file if the user is already signed + // in to the local profile, and there is no existing local credential cache. + // Used in scenarios where a user upgraded from an older version of Chrome + // that didn't support credential caching. This method is a no-op if local + // sync prefs have already been written to the local cache. + void WriteExistingSyncPrefsToLocalCache(); + + // Resets |alternate_store_| and schedules the next read from the alternate + // credential cache. + void ScheduleNextReadFromAlternateCredentialCache(); + protected: // Returns true if the credential cache represented by |store| contains a // value for |pref_name|. @@ -111,6 +121,50 @@ class CredentialCacheService : public ProfileKeyedService, } private: + // Used to track the initialization of the local credential cache. + class LocalStoreObserver + : public base::RefCounted<LocalStoreObserver>, + public PrefStore::Observer { + public: + LocalStoreObserver(CredentialCacheService* service, + scoped_refptr<JsonPrefStore> local_store); + virtual ~LocalStoreObserver(); + + // PrefStore::Observer implementation. + virtual void OnInitializationCompleted(bool succeeded) OVERRIDE; + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + + protected: + friend class base::RefCounted<LocalStoreObserver>; + + private: + CredentialCacheService* service_; + scoped_refptr<JsonPrefStore> local_store_; + DISALLOW_COPY_AND_ASSIGN(LocalStoreObserver); + }; + + // Used to track the initialization of the alternate credential cache. + class AlternateStoreObserver + : public base::RefCounted<AlternateStoreObserver>, + public PrefStore::Observer { + public: + AlternateStoreObserver(CredentialCacheService* service, + scoped_refptr<JsonPrefStore> alternate_store); + virtual ~AlternateStoreObserver(); + + // PrefStore::Observer implementation. + virtual void OnInitializationCompleted(bool succeeded) OVERRIDE; + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + + protected: + friend class base::RefCounted<AlternateStoreObserver>; + + private: + CredentialCacheService* service_; + scoped_refptr<JsonPrefStore> alternate_store_; + DISALLOW_COPY_AND_ASSIGN(AlternateStoreObserver); + }; + // Returns the path to the sync credentials file in the current profile // directory. FilePath GetCredentialPathInCurrentProfile() const; @@ -124,20 +178,9 @@ class CredentialCacheService : public ProfileKeyedService, // writer must be initialized, and false if not. bool ShouldInitializeLocalCredentialCacheWriter() const; - // Determines if we must look for credentials in the alternate profile, based - // on relevant sync preferences, in addition the to conditions in - // ShouldInitializeLocalCredentialCacheWriter(). Returns true if we must look - // for cached credentials, and false if not. - bool ShouldLookForCachedCredentialsInAlternateProfile() const; - // Initializes the JsonPrefStore object for the local profile directory. void InitializeLocalCredentialCacheWriter(); - // Initializes the JsonPrefStore object for the alternate profile directory - // if |should_initialize| is true. We take a bool* instead of a bool since - // this is a callback, and base::Owned needs to clean up the flag. - void InitializeAlternateCredentialCacheReader(bool* should_initialize); - // Returns true if there is an empty value for kGoogleServicesUsername in the // credential cache for the local profile (indicating that the user first // signed in and then signed out). Returns false if there's no value at all @@ -151,10 +194,6 @@ class CredentialCacheService : public ProfileKeyedService, // cannot auto-start. void LookForCachedCredentialsInAlternateProfile(); - // Loads cached sync credentials from the alternate profile and calls - // ApplyCachedCredentials if the load was successful. - void ReadCachedCredentialsFromAlternateProfile(); - // Initiates sync sign in using credentials read from the alternate profile by // persisting |google_services_username|, |encryption_bootstrap_token|, // |keep_everything_synced| and |preferred_types| to the local pref store, and @@ -211,10 +250,6 @@ class CredentialCacheService : public ProfileKeyedService, const std::string& sid, const std::string& encryption_bootstrap_token); - // Resets |alternate_store_| and schedules the next read from the alternate - // credential cache. - void ScheduleNextReadFromAlternateCredentialCache(); - // Profile for which credentials are being cached. Profile* profile_; @@ -226,10 +261,16 @@ class CredentialCacheService : public ProfileKeyedService, // it can be accessed by unit tests. scoped_refptr<JsonPrefStore> local_store_; + // Used to respond to the initialization of |local_store_|. + scoped_refptr<LocalStoreObserver> local_store_observer_; + // Used for read operations on the credential cache file in the alternate // profile directory. This is separate from the chrome pref store. scoped_refptr<JsonPrefStore> alternate_store_; + // Used to respond to the initialization of |alternate_store_|. + scoped_refptr<AlternateStoreObserver> alternate_store_observer_; + // Registrar for notifications from the PrefService. PrefChangeRegistrar pref_registrar_; diff --git a/chrome/browser/sync/credential_cache_service_win_unittest.cc b/chrome/browser/sync/credential_cache_service_win_unittest.cc index ea69b7f3..60cfa64 100644 --- a/chrome/browser/sync/credential_cache_service_win_unittest.cc +++ b/chrome/browser/sync/credential_cache_service_win_unittest.cc @@ -39,9 +39,6 @@ class CredentialCacheServiceTest : public CredentialCacheService, file_message_loop_.RunAllPending(); } - // PrefStore::Observer implementation. - virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {} - private: ScopedTempDir temp_dir_; MessageLoop file_message_loop_; |