blob: 23ac826ef192f9de27889e13a01a48976e48de0f [file] [log] [blame]
// 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_PREF_CHANGED: {
// One of the two sync encryption tokens has changed. Update its value in
// the local cache.
const std::string pref_name =
*(content::Details<const std::string>(details).ptr());
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 << ".";
}
break;
}
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<const TokenService::CredentialsUpdatedDetails>(
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::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<JsonPrefStore> 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<JsonPrefStore> 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<JsonPrefStore> 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<JsonPrefStore> 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<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) {
// 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<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(
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>(profile_));
registrar_.Add(this,
chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
content::Source<Profile>(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<ProfileSyncService>(service));
registrar_.Add(this,
chrome::NOTIFICATION_SYNC_CONFIGURE_START,
content::Source<ProfileSyncService>(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<TokenService>(token_service));
registrar_.Add(this,
chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
content::Source<TokenService>(token_service));
registrar_.Add(this,
chrome::NOTIFICATION_TOKENS_CLEARED,
content::Source<TokenService>(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