Commit b475ef4e authored by rsimha@google.com's avatar rsimha@google.com

Re-land 151364 - [sync] Auto-create credential cache for users who are already...

Re-land 151364 - [sync] Auto-create credential cache for users who are already signed in and go on to upgrade Chrome

Originally reverted due to crashes in GetLastUpdatedTime(). See crbug.com/143214. Turns out 151364 wasn't the root cause. Feature has been disabled via switch on canary. Root cause fix is under review in a separate patch. Re-landing 151364.

Original description:

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,143214
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.

Original review URL: https://chromiumcodereview.appspot.com/10830239
First revert review URL: https://chromiumcodereview.appspot.com/10830362
TBR=zea@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10834395

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152122 0039d316-1c4b-4281-b951-d872f2087c98
parent c0679892
...@@ -177,6 +177,10 @@ class TokenService : public GaiaAuthConsumer, ...@@ -177,6 +177,10 @@ class TokenService : public GaiaAuthConsumer,
void IssueAuthTokenForTest(const std::string& service, void IssueAuthTokenForTest(const std::string& service,
const std::string& auth_token); const std::string& auth_token);
const GaiaAuthConsumer::ClientLoginResult& credentials() const {
return credentials_;
}
// GaiaAuthConsumer implementation. // GaiaAuthConsumer implementation.
virtual void OnIssueAuthTokenSuccess(const std::string& service, virtual void OnIssueAuthTokenSuccess(const std::string& service,
const std::string& auth_token) OVERRIDE; const std::string& auth_token) OVERRIDE;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/signin_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.h"
#include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/signin/token_service_factory.h"
#include "chrome/browser/sync/glue/chrome_encryptor.h" #include "chrome/browser/sync/glue/chrome_encryptor.h"
...@@ -55,7 +56,11 @@ CredentialCacheService::CredentialCacheService(Profile* profile) ...@@ -55,7 +56,11 @@ CredentialCacheService::CredentialCacheService(Profile* profile)
weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
if (profile_) { if (profile_) {
InitializeLocalCredentialCacheWriter(); 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(); LookForCachedCredentialsInAlternateProfile();
} }
} }
...@@ -65,32 +70,10 @@ CredentialCacheService::~CredentialCacheService() { ...@@ -65,32 +70,10 @@ CredentialCacheService::~CredentialCacheService() {
} }
void CredentialCacheService::Shutdown() { void CredentialCacheService::Shutdown() {
if (local_store_.get()) { local_store_observer_.release();
local_store_.release(); local_store_.release();
} alternate_store_observer_.release();
alternate_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.
} }
void CredentialCacheService::Observe( void CredentialCacheService::Observe(
...@@ -103,11 +86,9 @@ void CredentialCacheService::Observe( ...@@ -103,11 +86,9 @@ void CredentialCacheService::Observe(
case chrome::NOTIFICATION_PREF_CHANGED: { case chrome::NOTIFICATION_PREF_CHANGED: {
const std::string pref_name = const std::string pref_name =
*(content::Details<const std::string>(details).ptr()); *(content::Details<const std::string>(details).ptr());
if (pref_name == prefs::kGoogleServicesUsername || if (pref_name == prefs::kSyncEncryptionBootstrapToken) {
pref_name == prefs::kSyncEncryptionBootstrapToken) { PackAndUpdateStringPref(pref_name,
PackAndUpdateStringPref( sync_prefs_.GetEncryptionBootstrapToken());
pref_name,
profile_->GetPrefs()->GetString(pref_name.c_str()));
} else { } else {
UpdateBooleanPref(pref_name, UpdateBooleanPref(pref_name,
profile_->GetPrefs()->GetBoolean(pref_name.c_str())); profile_->GetPrefs()->GetBoolean(pref_name.c_str()));
...@@ -115,6 +96,36 @@ void CredentialCacheService::Observe( ...@@ -115,6 +96,36 @@ void CredentialCacheService::Observe(
break; 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: { case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: {
const TokenService::CredentialsUpdatedDetails& token_details = const TokenService::CredentialsUpdatedDetails& token_details =
*(content::Details<const TokenService::CredentialsUpdatedDetails>( *(content::Details<const TokenService::CredentialsUpdatedDetails>(
...@@ -137,6 +148,152 @@ void CredentialCacheService::Observe( ...@@ -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, bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store,
const std::string& pref_name) { const std::string& pref_name) {
return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK);
...@@ -263,6 +420,65 @@ bool CredentialCacheService::GetBooleanPref( ...@@ -263,6 +420,65 @@ bool CredentialCacheService::GetBooleanPref(
return 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) {
// 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 { FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const {
// The sync credential path in the default Desktop profile is // The sync credential path in the default Desktop profile is
// "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while
...@@ -281,31 +497,26 @@ FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { ...@@ -281,31 +497,26 @@ FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const {
return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); 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() { void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
local_store_ = new JsonPrefStore( local_store_ = new JsonPrefStore(
GetCredentialPathInCurrentProfile(), GetCredentialPathInCurrentProfile(),
content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE)); content::BrowserThread::FILE));
local_store_->AddObserver(this); local_store_observer_ = new LocalStoreObserver(this, local_store_);
local_store_->ReadPrefsAsync(NULL); 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. // stored in the PrefStore.
pref_registrar_.Init(profile_->GetPrefs()); pref_registrar_.Init(profile_->GetPrefs());
pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this);
pref_registrar_.Add(prefs::kGoogleServicesUsername, this);
pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this);
ModelTypeSet all_types = syncer::ModelTypeSet::All(); ModelTypeSet all_types = syncer::ModelTypeSet::All();
for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
...@@ -319,6 +530,9 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() { ...@@ -319,6 +530,9 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
// Register for notifications for updates to lsid and sid, which are stored in // Register for notifications for updates to lsid and sid, which are stored in
// the TokenService. // the TokenService.
TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
registrar_.Add(this,
chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
content::Source<TokenService>(token_service));
registrar_.Add(this, registrar_.Add(this,
chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
content::Source<TokenService>(token_service)); content::Source<TokenService>(token_service));
...@@ -327,24 +541,16 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() { ...@@ -327,24 +541,16 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
content::Source<TokenService>(token_service)); content::Source<TokenService>(token_service));
} }
void CredentialCacheService::InitializeAlternateCredentialCacheReader( void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
bool* should_initialize) { // Attempt to read cached credentials from the alternate profile. If no file
// If |should_initialize| is false, there was no credential cache in the // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned
// alternate profile directory, and there is nothing to do right now. Schedule // after initialization is complete.
// 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.
alternate_store_ = new JsonPrefStore( alternate_store_ = new JsonPrefStore(
GetCredentialPathInAlternateProfile(), GetCredentialPathInAlternateProfile(),
content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE)); content::BrowserThread::FILE));
alternate_store_->AddObserver(this); alternate_store_observer_ = new AlternateStoreObserver(this,
alternate_store_);
alternate_store_->ReadPrefsAsync(NULL); alternate_store_->ReadPrefsAsync(NULL);
} }
...@@ -358,136 +564,6 @@ bool CredentialCacheService::HasUserSignedOut() { ...@@ -358,136 +564,6 @@ bool CredentialCacheService::HasUserSignedOut() {
prefs::kGoogleServicesUsername).empty(); 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( void CredentialCacheService::InitiateSignInWithCachedCredentials(
const std::string& google_services_username, const std::string& google_services_username,
const std::string& encryption_bootstrap_token, const std::string& encryption_bootstrap_token,
...@@ -607,21 +683,4 @@ bool CredentialCacheService::ShouldSignInToSync( ...@@ -607,21 +683,4 @@ bool CredentialCacheService::ShouldSignInToSync(
!service->setup_in_progress(); !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 } // namespace syncer
...@@ -39,8 +39,7 @@ namespace syncer { ...@@ -39,8 +39,7 @@ namespace syncer {
// sync using credentials that were cached due to signing in on the other // sync using credentials that were cached due to signing in on the other
// (alternate) mode. // (alternate) mode.
class CredentialCacheService : public ProfileKeyedService, class CredentialCacheService : public ProfileKeyedService,
public content::NotificationObserver, public content::NotificationObserver {
public PrefStore::Observer {
public: public:
explicit CredentialCacheService(Profile* profile); explicit CredentialCacheService(Profile* profile);
virtual ~CredentialCacheService(); virtual ~CredentialCacheService();
...@@ -48,15 +47,26 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -48,15 +47,26 @@ class CredentialCacheService : public ProfileKeyedService,
// ProfileKeyedService implementation. // ProfileKeyedService implementation.
virtual void Shutdown() OVERRIDE; virtual void Shutdown() OVERRIDE;
// PrefStore::Observer implementation.
virtual void OnInitializationCompleted(bool succeeded) OVERRIDE;
virtual void OnPrefValueChanged(const std::string& key) OVERRIDE;
// content::NotificationObserver implementation. // content::NotificationObserver implementation.
virtual void Observe(int type, virtual void Observe(int type,
const content::NotificationSource& source, const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE; 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: protected:
// Returns true if the credential cache represented by |store| contains a // Returns true if the credential cache represented by |store| contains a
// value for |pref_name|. // value for |pref_name|.
...@@ -111,6 +121,50 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -111,6 +121,50 @@ class CredentialCacheService : public ProfileKeyedService,
} }
private: 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 // Returns the path to the sync credentials file in the current profile
// directory. // directory.
FilePath GetCredentialPathInCurrentProfile() const; FilePath GetCredentialPathInCurrentProfile() const;
...@@ -124,20 +178,9 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -124,20 +178,9 @@ class CredentialCacheService : public ProfileKeyedService,
// writer must be initialized, and false if not. // writer must be initialized, and false if not.
bool ShouldInitializeLocalCredentialCacheWriter() const; 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. // Initializes the JsonPrefStore object for the local profile directory.
void InitializeLocalCredentialCacheWriter(); 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 // Returns true if there is an empty value for kGoogleServicesUsername in the
// credential cache for the local profile (indicating that the user first // 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 // signed in and then signed out). Returns false if there's no value at all
...@@ -151,10 +194,6 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -151,10 +194,6 @@ class CredentialCacheService : public ProfileKeyedService,
// cannot auto-start. // cannot auto-start.
void LookForCachedCredentialsInAlternateProfile(); 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 // Initiates sync sign in using credentials read from the alternate profile by
// persisting |google_services_username|, |encryption_bootstrap_token|, // persisting |google_services_username|, |encryption_bootstrap_token|,
// |keep_everything_synced| and |preferred_types| to the local pref store, and // |keep_everything_synced| and |preferred_types| to the local pref store, and
...@@ -211,10 +250,6 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -211,10 +250,6 @@ class CredentialCacheService : public ProfileKeyedService,
const std::string& sid, const std::string& sid,
const std::string& encryption_bootstrap_token); 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 for which credentials are being cached.
Profile* profile_; Profile* profile_;
...@@ -226,10 +261,16 @@ class CredentialCacheService : public ProfileKeyedService, ...@@ -226,10 +261,16 @@ class CredentialCacheService : public ProfileKeyedService,
// it can be accessed by unit tests. // it can be accessed by unit tests.
scoped_refptr<JsonPrefStore> local_store_; 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 // Used for read operations on the credential cache file in the alternate
// profile directory. This is separate from the chrome pref store. // profile directory. This is separate from the chrome pref store.
scoped_refptr<JsonPrefStore> alternate_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. // Registrar for notifications from the PrefService.
PrefChangeRegistrar pref_registrar_; PrefChangeRegistrar pref_registrar_;
......
...@@ -39,9 +39,6 @@ class CredentialCacheServiceTest : public CredentialCacheService, ...@@ -39,9 +39,6 @@ class CredentialCacheServiceTest : public CredentialCacheService,
file_message_loop_.RunAllPending(); file_message_loop_.RunAllPending();
} }
// PrefStore::Observer implementation.
virtual void OnInitializationCompleted(bool succeeded) OVERRIDE {}
private: private:
ScopedTempDir temp_dir_; ScopedTempDir temp_dir_;
MessageLoop file_message_loop_; MessageLoop file_message_loop_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment