// Copyright (c) 2012 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/credential_cache_service_win.h" #include "base/base64.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/file_util.h" #include "base/string_number_conversions.h" #include "base/time.h" #include "base/values.h" #include "base/win/windows_version.h" #include "chrome/browser/prefs/pref_service.h" #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/credential_cache_service_factory_win.h" #include "chrome/browser/sync/glue/chrome_encryptor.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_constants.h" #include "sync/internal_api/public/base/model_type.h" namespace syncer { // The time delay (in seconds) between two consecutive polls of the alternate // credential cache. A two minute delay seems like a reasonable amount of time // in which to propagate changes to signed in state between Metro and Desktop. const int kCredentialCachePollIntervalSecs = 2 * 60; // Keeps track of the last time a credential cache was written to. Used to make // sure that we only apply changes from newer credential caches to older ones, // and not vice versa. const char kLastCacheUpdateTimeKey[] = "last_cache_update_time"; // Deprecated. We were previously using base::TimeTicks as a timestamp. This was // bad because TimeTicks values roll over on machine restart. We now use // base::Time as a timestamp. However, we can't simply reuse "last_updated_time" // because it may result in a Time value being compared with a TimeTicks value. const char kLastUpdatedTimeTicksDeprecated[] = "last_updated_time"; using base::TimeDelta; using content::BrowserThread; CredentialCacheService::CredentialCacheService(Profile* profile) : profile_(profile), // |profile_| is null in unit tests. sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { if (profile_) { InitializeLocalCredentialCacheWriter(); // 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(); } } CredentialCacheService::~CredentialCacheService() { Shutdown(); } void CredentialCacheService::Shutdown() { if (local_store_.get()) local_store_->CommitPendingWrite(); local_store_observer_.release(); local_store_.release(); alternate_store_observer_.release(); alternate_store_.release(); } void CredentialCacheService::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(local_store_.get()); switch (type) { case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: { // The user has signed out. Write blank values to the google username, // encryption tokens and token service credentials in the local cache. PackAndUpdateStringPref(prefs::kGoogleServicesUsername, std::string()); if (HasPref(local_store_, prefs::kSyncEncryptionBootstrapToken)) { PackAndUpdateStringPref(prefs::kSyncEncryptionBootstrapToken, std::string()); } if (HasPref(local_store_, prefs::kSyncKeystoreEncryptionBootstrapToken)) { PackAndUpdateStringPref(prefs::kSyncKeystoreEncryptionBootstrapToken, std::string()); } PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); break; } case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: { // The user has signed in. Write the new value of the google username to // the local cache. SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); PackAndUpdateStringPref(prefs::kGoogleServicesUsername, signin->GetAuthenticatedUsername()); break; } case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE: { // Local sync configuration is done. The sync service is now ready to // be reconfigured. Immediately look for any unconsumed config changes in // the alternate profile. If all changes have been consumed, this is a // no-op. ScheduleNextReadFromAlternateCredentialCache(0); break; } case chrome::NOTIFICATION_SYNC_CONFIGURE_START: { // We have detected a sync sign in, auto-start or reconfigure. Write the // latest sync preferences to the local cache. WriteSyncPrefsToLocalCache(); break; } case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { // The token service has been fully initialized. Update the token service // credentials in the local cache. This is a no-op if the cache already // contains the latest values. 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: { // The token service has new credentials. Write them to the local cache. const TokenService::CredentialsUpdatedDetails& token_details = *(content::Details( details).ptr()); PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); break; } case chrome::NOTIFICATION_TOKENS_CLEARED: { // Tokens have been cleared. Blank out lsid and sid in the local cache. PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); break; } default: { NOTREACHED(); break; } } } void CredentialCacheService::OnPreferenceChanged(PrefServiceBase* service, const std::string& pref_name) { // One of the two sync encryption tokens has changed. Update its value in // the local cache. if (pref_name == prefs::kSyncEncryptionBootstrapToken) { PackAndUpdateStringPref(pref_name, sync_prefs_.GetEncryptionBootstrapToken()); } else if (pref_name == prefs::kSyncKeystoreEncryptionBootstrapToken) { PackAndUpdateStringPref( pref_name, sync_prefs_.GetKeystoreEncryptionBootstrapToken()); } else { NOTREACHED() "Invalid pref name " << pref_name << "."; } } 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. Note that it is sufficient to // have just one of the two sync encryption tokens. 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::kSyncKeystoreEncryptionBootstrapToken)) || !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { VLOG(1) << "Could not find cached credentials in \"" << GetCredentialPathInAlternateProfile().value() << "\"."; ScheduleNextReadFromAlternateCredentialCache( kCredentialCachePollIntervalSecs); return; } // Extract the google username, lsid and sid from the alternate credential // cache. std::string alternate_google_services_username = GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); std::string alternate_lsid = GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); std::string alternate_sid = GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); // Extract the sync encryption tokens from the alternate credential cache. // Both tokens may not be found, since only one of them is used at any time. std::string alternate_encryption_bootstrap_token; if (HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken)) { alternate_encryption_bootstrap_token = GetAndUnpackStringPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken); } std::string alternate_keystore_encryption_bootstrap_token; if (HasPref(alternate_store_, prefs::kSyncKeystoreEncryptionBootstrapToken)) { alternate_keystore_encryption_bootstrap_token = GetAndUnpackStringPref(alternate_store_, prefs::kSyncKeystoreEncryptionBootstrapToken); } // 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(alternate_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 alternate_keep_everything_synced = GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); ModelTypeSet registered_types = service->GetRegisteredDataTypes(); ModelTypeSet alternate_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)) alternate_preferred_types.Put(it.Get()); } // Reconfigure if sync settings, encryption tokens or token service // credentials have changed in the alternate profile, but for the same user // that is signed in to the local profile. if (MayReconfigureSync(alternate_google_services_username)) { if (HaveSyncPrefsChanged(alternate_keep_everything_synced, alternate_preferred_types)) { VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; service->OnUserChoseDatatypes(alternate_keep_everything_synced, alternate_preferred_types); } if (HaveSyncEncryptionTokensChanged( alternate_encryption_bootstrap_token, alternate_keystore_encryption_bootstrap_token)) { VLOG(1) << "Sync encryption tokens have changed in other profile."; sync_prefs_.SetEncryptionBootstrapToken( alternate_encryption_bootstrap_token); sync_prefs_.SetKeystoreEncryptionBootstrapToken( alternate_keystore_encryption_bootstrap_token); } if (HaveTokenServiceCredentialsChanged(alternate_lsid, alternate_sid)) { VLOG(1) << "Token service credentials have changed in other profile."; UpdateTokenServiceCredentials(alternate_lsid, alternate_sid); } } // Sign in if we notice new cached credentials in the alternate profile. if (ShouldSignInToSync(alternate_google_services_username, alternate_lsid, alternate_sid, alternate_encryption_bootstrap_token, alternate_keystore_encryption_bootstrap_token)) { InitiateSignInWithCachedCredentials( alternate_google_services_username, alternate_encryption_bootstrap_token, alternate_keystore_encryption_bootstrap_token, alternate_keep_everything_synced, alternate_preferred_types); UpdateTokenServiceCredentials(alternate_lsid, alternate_sid); } // Schedule the next read from the alternate credential cache so that we can // detect future reconfigures or sign outs. ScheduleNextReadFromAlternateCredentialCache( kCredentialCachePollIntervalSecs); } void CredentialCacheService::WriteSyncPrefsToLocalCache() { 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( int delay_secs) { DCHECK_LE(0, delay_secs); // 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(delay_secs)); } bool CredentialCacheService::HasPref(scoped_refptr store, const std::string& pref_name) { return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); } // static base::StringValue* CredentialCacheService::PackCredential( const std::string& credential) { // Do nothing for empty credentials. if (credential.empty()) return base::Value::CreateStringValue(""); browser_sync::ChromeEncryptor encryptor; std::string encrypted; if (!encryptor.EncryptString(credential, &encrypted)) { NOTREACHED(); return base::Value::CreateStringValue(std::string()); } std::string encoded; if (!base::Base64Encode(encrypted, &encoded)) { NOTREACHED(); return base::Value::CreateStringValue(std::string()); } return base::Value::CreateStringValue(encoded); } // static std::string CredentialCacheService::UnpackCredential( const base::Value& packed) { std::string encoded; if (!packed.GetAsString(&encoded)) { NOTREACHED(); return std::string(); } // Do nothing for empty credentials. if (encoded.empty()) return std::string(); std::string encrypted; if (!base::Base64Decode(encoded, &encrypted)) { NOTREACHED(); return std::string(); } browser_sync::ChromeEncryptor encryptor; std::string unencrypted; if (!encryptor.DecryptString(encrypted, &unencrypted)) { NOTREACHED(); return std::string(); } return unencrypted; } void CredentialCacheService::WriteLastCacheUpdateTime() { DCHECK(local_store_.get()); int64 last_cache_update_time_int64 = base::Time::Now().ToInternalValue(); std::string last_cache_update_time_string = base::Int64ToString(last_cache_update_time_int64); local_store_->SetValueSilently( kLastCacheUpdateTimeKey, base::Value::CreateStringValue(last_cache_update_time_string)); } void CredentialCacheService::PackAndUpdateStringPref( const std::string& pref_name, const std::string& new_value) { DCHECK(local_store_.get()); if (HasPref(local_store_, pref_name) && GetAndUnpackStringPref(local_store_, pref_name) == new_value) { return; } if (!HasUserSignedOut()) { local_store_->SetValueSilently(pref_name, PackCredential(new_value)); } else { // Write a blank value since we cache credentials only for first-time // sign-ins. local_store_->SetValueSilently(pref_name, PackCredential(std::string())); } WriteLastCacheUpdateTime(); } void CredentialCacheService::UpdateBooleanPref(const std::string& pref_name, bool new_value) { DCHECK(local_store_.get()); if (HasPref(local_store_, pref_name) && GetBooleanPref(local_store_, pref_name) == new_value) { return; } if (!HasUserSignedOut()) { local_store_->SetValueSilently(pref_name, base::Value::CreateBooleanValue(new_value)); } else { // Write a default value of false since we cache credentials only for // first-time sign-ins. local_store_->SetValueSilently(pref_name, base::Value::CreateBooleanValue(false)); } WriteLastCacheUpdateTime(); } base::Time CredentialCacheService::GetLastCacheUpdateTime( scoped_refptr store) { DCHECK(HasPref(store, kLastCacheUpdateTimeKey)); const base::Value* last_cache_update_time_value = NULL; store->GetValue(kLastCacheUpdateTimeKey, &last_cache_update_time_value); DCHECK(last_cache_update_time_value); std::string last_cache_update_time_string; last_cache_update_time_value->GetAsString(&last_cache_update_time_string); int64 last_cache_update_time_int64; bool success = base::StringToInt64(last_cache_update_time_string, &last_cache_update_time_int64); DCHECK(success); return base::Time::FromInternalValue(last_cache_update_time_int64); } bool CredentialCacheService::AlternateCacheIsMoreRecent() { DCHECK(alternate_store_.get()); // If the alternate credential cache doesn't have the "last_cache_update_time" // field, it was written by an older version of chrome, and we therefore // consider the local cache to be more recent. if (!HasPref(alternate_store_, kLastCacheUpdateTimeKey)) return false; DCHECK(HasPref(local_store_, kLastCacheUpdateTimeKey)); return GetLastCacheUpdateTime(alternate_store_) > GetLastCacheUpdateTime(local_store_); } std::string CredentialCacheService::GetAndUnpackStringPref( scoped_refptr store, const std::string& pref_name) { const base::Value* pref_value = NULL; store->GetValue(pref_name, &pref_value); return UnpackCredential(*pref_value); } bool CredentialCacheService::GetBooleanPref( scoped_refptr store, const std::string& pref_name) { const base::Value* pref_value = NULL; store->GetValue(pref_name, &pref_value); bool pref; pref_value->GetAsBoolean(&pref); return pref; } CredentialCacheService::LocalStoreObserver::LocalStoreObserver( CredentialCacheService* service, scoped_refptr 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) { // Note that |succeeded| will be true even if the local cache file wasn't // found, so long as its parent dir (the chrome profile directory) was found. // If |succeeded| is false, it means that the chrome profile directory is // missing. In this case, there's nothing we can do other than DCHECK. DCHECK(succeeded); // During startup, we do a precautionary write of the google username, // encryption tokens, sync prefs and last cache update time to the local cache // in order to recover from the following cases: // 1) There is no local credential cache, but the user is signed in. This // could happen if a signed-in user restarts chrome after upgrading from // an older version that didn't support credential caching. // 2) There is a local credential cache, but we missed writing sync credential // updates to it in the past due to a crash, or due to the user exiting // chrome in the midst of a sign in, sign out or reconfigure. // 3) There is a local credential cache that was written to by an older // version of Chrome, and it does not contain the "last_cache_update_time" // field. // Note: If the local credential cache was already up-to-date, the operations // below will be no-ops, and won't change the cache's last updated time. Also, // if the user is not signed in and there is no local credential cache, we // don't want to create a cache with empty values. SigninManager* signin = SigninManagerFactory::GetForProfile(service_->profile_); if ((local_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NO_FILE && !signin->GetAuthenticatedUsername().empty()) || (local_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE)) { service_->PackAndUpdateStringPref(prefs::kGoogleServicesUsername, signin->GetAuthenticatedUsername()); if (!service_->sync_prefs_.GetEncryptionBootstrapToken().empty()) { service_->PackAndUpdateStringPref( prefs::kSyncEncryptionBootstrapToken, service_->sync_prefs_.GetEncryptionBootstrapToken()); } if (!service_->sync_prefs_.GetKeystoreEncryptionBootstrapToken().empty()) { service_->PackAndUpdateStringPref( prefs::kSyncKeystoreEncryptionBootstrapToken, service_->sync_prefs_.GetKeystoreEncryptionBootstrapToken()); } service_->WriteSyncPrefsToLocalCache(); if (!service_->HasPref(local_store_, kLastCacheUpdateTimeKey)) service_->WriteLastCacheUpdateTime(); if (service_->HasPref(local_store_, kLastUpdatedTimeTicksDeprecated)) local_store_->RemoveValue(kLastUpdatedTimeTicksDeprecated); } // Now that the local credential cache is ready, start listening for events // associated with various sync config changes. service_->StartListeningForSyncConfigChanges(); } void CredentialCacheService::LocalStoreObserver::OnPrefValueChanged( const std::string& key) { // Nothing to do here, since credentials are cached silently. } CredentialCacheService::AlternateStoreObserver::AlternateStoreObserver( CredentialCacheService* service, scoped_refptr 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( kCredentialCachePollIntervalSecs); } } 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 // the sync credential path in the default Metro profile is // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". DCHECK(profile_); return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); } FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { DCHECK(profile_); FilePath alternate_user_data_dir; chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); // TODO(rsimha): This code path is to allow for testing in the presence of // strange singleton mode. Delete this block before shipping. // See http://crbug.com/144280. const CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kEnableSyncCredentialCaching) && !CredentialCacheServiceFactory::IsDefaultProfile(profile_)) { DCHECK(CredentialCacheServiceFactory::IsDefaultAlternateProfileForTest( profile_)); chrome::GetDefaultUserDataDirectory(&alternate_user_data_dir); } FilePath alternate_default_profile_dir = ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); } void CredentialCacheService::InitializeLocalCredentialCacheWriter() { local_store_ = new JsonPrefStore( GetCredentialPathInCurrentProfile(), content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::FILE)); local_store_observer_ = new LocalStoreObserver(this, local_store_); local_store_->ReadPrefsAsync(NULL); } void CredentialCacheService::StartListeningForSyncConfigChanges() { // Register for notifications for google sign in and sign out. registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, content::Source(profile_)); registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, content::Source(profile_)); // Register for notifications for sync configuration changes that could occur // during sign in or reconfiguration. ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE, content::Source(service)); registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_START, content::Source(service)); // Register for notifications for updates to the sync encryption tokens, which // are stored in the PrefStore. pref_registrar_.Init(profile_->GetPrefs()); pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); pref_registrar_.Add(prefs::kSyncKeystoreEncryptionBootstrapToken, this); // Register for notifications for updates to lsid and sid, which are stored in // the TokenService. TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); registrar_.Add(this, chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, content::Source(token_service)); registrar_.Add(this, chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, content::Source(token_service)); registrar_.Add(this, chrome::NOTIFICATION_TOKENS_CLEARED, content::Source(token_service)); } 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_observer_ = new AlternateStoreObserver(this, alternate_store_); alternate_store_->ReadPrefsAsync(NULL); } bool CredentialCacheService::HasUserSignedOut() { DCHECK(local_store_.get()); // If HasPref() is false, the user never signed in, since there are no // previously cached credentials. If the kGoogleServicesUsername pref is // empty, it means that the user signed in and subsequently signed out. return HasPref(local_store_, prefs::kGoogleServicesUsername) && GetAndUnpackStringPref(local_store_, prefs::kGoogleServicesUsername).empty(); } void CredentialCacheService::InitiateSignInWithCachedCredentials( const std::string& google_services_username, const std::string& encryption_bootstrap_token, const std::string& keystore_encryption_bootstrap_token, bool keep_everything_synced, ModelTypeSet preferred_types) { // Update the google username in the SigninManager and PrefStore. Also update // its value in the local credential cache, since we will not send out // NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL in this case. ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); service->signin()->SetAuthenticatedUsername(google_services_username); profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, google_services_username); PackAndUpdateStringPref(prefs::kGoogleServicesUsername, service->signin()->GetAuthenticatedUsername()); // Update sync encryption tokens after making sure at least one of them is // non-empty. DCHECK(!encryption_bootstrap_token.empty() || !keystore_encryption_bootstrap_token.empty()); if (!encryption_bootstrap_token.empty()) { sync_prefs_.SetEncryptionBootstrapToken(encryption_bootstrap_token); } if (!keystore_encryption_bootstrap_token.empty()) { sync_prefs_.SetKeystoreEncryptionBootstrapToken( keystore_encryption_bootstrap_token); } // Update the sync preferences. sync_prefs_.SetStartSuppressed(false); sync_prefs_.SetSyncSetupCompleted(); sync_prefs_.SetKeepEverythingSynced(keep_everything_synced); sync_prefs_.SetPreferredDataTypes(service->GetRegisteredDataTypes(), preferred_types); } void CredentialCacheService::UpdateTokenServiceCredentials( const std::string& alternate_lsid, const std::string& alternate_sid) { GaiaAuthConsumer::ClientLoginResult login_result; login_result.lsid = alternate_lsid; login_result.sid = alternate_sid; TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); token_service->UpdateCredentials(login_result); DCHECK(token_service->AreCredentialsValid()); token_service->StartFetchingTokens(); } void CredentialCacheService::InitiateSignOut() { ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); service->DisableForUser(); } bool CredentialCacheService::HaveSyncPrefsChanged( bool alternate_keep_everything_synced, ModelTypeSet alternate_preferred_types) const { if (alternate_keep_everything_synced && sync_prefs_.HasKeepEverythingSynced()) { return false; } ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); ModelTypeSet local_preferred_types = sync_prefs_.GetPreferredDataTypes(service->GetRegisteredDataTypes()); return (alternate_keep_everything_synced != sync_prefs_.HasKeepEverythingSynced()) || !alternate_preferred_types.Equals(local_preferred_types); } bool CredentialCacheService::HaveSyncEncryptionTokensChanged( const std::string& alternate_encryption_bootstrap_token, const std::string& alternate_keystore_encryption_bootstrap_token) { std::string local_encryption_bootstrap_token; if (HasPref(local_store_, prefs::kSyncEncryptionBootstrapToken)) { local_encryption_bootstrap_token = GetAndUnpackStringPref(local_store_, prefs::kSyncEncryptionBootstrapToken); } std::string local_keystore_encryption_bootstrap_token; if (HasPref(local_store_, prefs::kSyncKeystoreEncryptionBootstrapToken)) { local_keystore_encryption_bootstrap_token = GetAndUnpackStringPref(local_store_, prefs::kSyncKeystoreEncryptionBootstrapToken); } return (local_encryption_bootstrap_token != alternate_encryption_bootstrap_token) || (local_keystore_encryption_bootstrap_token != alternate_keystore_encryption_bootstrap_token); } bool CredentialCacheService::HaveTokenServiceCredentialsChanged( const std::string& alternate_lsid, const std::string& alternate_sid) { std::string local_lsid = GetAndUnpackStringPref(local_store_, GaiaConstants::kGaiaLsid); std::string local_sid = GetAndUnpackStringPref(local_store_, GaiaConstants::kGaiaSid); return local_lsid != alternate_lsid || local_sid != alternate_sid; } bool CredentialCacheService::ShouldSignOutOfSync( const std::string& alternate_google_services_username) { // We must sign out of sync iff: // 1) The user is signed in to the local profile. // 2) The user has never signed out of the local profile in the past. // 3) We noticed that the user has signed out of the alternate profile. // 4) The user is not already in the process of configuring sync. // 5) The alternate cache was updated more recently than the local cache. ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); return !service->signin()->GetAuthenticatedUsername().empty() && !HasUserSignedOut() && alternate_google_services_username.empty() && !service->setup_in_progress() && AlternateCacheIsMoreRecent(); } bool CredentialCacheService::MayReconfigureSync( const std::string& alternate_google_services_username) { // We may attempt to reconfigure sync iff: // 1) The user is signed in to the local profile. // 2) The user has never signed out of the local profile in the past. // 3) The user is signed in to the alternate profile with the same account. // 4) The user is not already in the process of configuring sync. // 5) The alternate cache was updated more recently than the local cache. // 6) The sync backend is initialized and ready to consume config changes. ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); return !service->signin()->GetAuthenticatedUsername().empty() && !HasUserSignedOut() && (alternate_google_services_username == service->signin()->GetAuthenticatedUsername()) && !service->setup_in_progress() && AlternateCacheIsMoreRecent() && service->ShouldPushChanges(); } bool CredentialCacheService::ShouldSignInToSync( const std::string& alternate_google_services_username, const std::string& alternate_lsid, const std::string& alternate_sid, const std::string& alternate_encryption_bootstrap_token, const std::string& alternate_keystore_encryption_bootstrap_token) { // We should sign in with cached credentials from the alternate profile iff: // 1) The user is not currently signed in to the local profile. // 2) The user has never signed out of the local profile in the past. // 3) Valid cached credentials are available in the alternate profile. // 4) The user is not already in the process of configuring sync. ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(profile_); return service->signin()->GetAuthenticatedUsername().empty() && !HasUserSignedOut() && !alternate_google_services_username.empty() && !alternate_lsid.empty() && !alternate_sid.empty() && !(alternate_encryption_bootstrap_token.empty() && alternate_keystore_encryption_bootstrap_token.empty()) && !service->setup_in_progress(); } } // namespace syncer