Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2226)

Unified Diff: chrome/browser/sync/credential_cache_service_win.cc

Issue 10828108: [sync] Add a polling mechanism to CredentialCacheService for on-the-fly sign in / sign out / reconf… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More CR Feedback + minor code reuse. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/sync/credential_cache_service_win.h ('k') | chrome/browser/sync/profile_sync_service.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/sync/credential_cache_service_win.cc
diff --git a/chrome/browser/sync/credential_cache_service_win.cc b/chrome/browser/sync/credential_cache_service_win.cc
index bd5e8de7aae84c37e04ab1b041cbf279614a0a5a..6dc7eeefb6be4c902cde7184b1862dadf9df295a 100644
--- a/chrome/browser/sync/credential_cache_service_win.cc
+++ b/chrome/browser/sync/credential_cache_service_win.cc
@@ -9,6 +9,8 @@
#include "base/base64.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"
@@ -33,6 +35,17 @@
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 kLastUpdatedTime[] = "last_updated_time";
+
+using base::TimeDelta;
using content::BrowserThread;
CredentialCacheService::CredentialCacheService(Profile* profile)
@@ -64,9 +77,14 @@ void CredentialCacheService::Shutdown() {
void CredentialCacheService::OnInitializationCompleted(bool succeeded) {
DCHECK(succeeded);
- // When the alternate credential store becomes available, begin consuming its
- // cached credentials.
- if (alternate_store_.get() && alternate_store_->IsInitializationComplete()) {
+ // 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();
}
}
@@ -176,6 +194,15 @@ std::string CredentialCacheService::UnpackCredential(
return unencrypted;
}
+void CredentialCacheService::WriteLastUpdatedTime() {
+ DCHECK(local_store_.get());
+ int64 last_updated_time = base::TimeTicks::Now().ToInternalValue();
+ std::string last_updated_time_string = base::Int64ToString(last_updated_time);
+ local_store_->SetValueSilently(
+ kLastUpdatedTime,
+ base::Value::CreateStringValue(last_updated_time_string));
+}
+
void CredentialCacheService::PackAndUpdateStringPref(
const std::string& pref_name,
const std::string& new_value) {
@@ -187,6 +214,7 @@ void CredentialCacheService::PackAndUpdateStringPref(
// sign-ins.
local_store_->SetValueSilently(pref_name, PackCredential(std::string()));
}
+ WriteLastUpdatedTime();
}
void CredentialCacheService::UpdateBooleanPref(const std::string& pref_name,
@@ -194,13 +222,27 @@ void CredentialCacheService::UpdateBooleanPref(const std::string& pref_name,
DCHECK(local_store_.get());
if (!HasUserSignedOut()) {
local_store_->SetValueSilently(pref_name,
- base::Value::CreateBooleanValue(new_value));
+ 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));
}
+ WriteLastUpdatedTime();
+}
+
+int64 CredentialCacheService::GetLastUpdatedTime(
+ scoped_refptr<JsonPrefStore> store) {
+ const base::Value* last_updated_time_value = NULL;
+ store->GetValue(kLastUpdatedTime, &last_updated_time_value);
+ std::string last_updated_time_string;
+ last_updated_time_value->GetAsString(&last_updated_time_string);
+ int64 last_updated_time;
+ bool success = base::StringToInt64(last_updated_time_string,
+ &last_updated_time);
+ DCHECK(success);
+ return last_updated_time;
}
std::string CredentialCacheService::GetAndUnpackStringPref(
@@ -245,13 +287,10 @@ bool CredentialCacheService::ShouldLookForCachedCredentialsInAlternateProfile()
// true:
// 1) Sync is not disabled by policy.
// 2) Sync startup is not suppressed.
- // 3) No user is currently signed in to sync.
- DCHECK(profile_);
- PrefService* prefs = profile_->GetPrefs();
- DCHECK(prefs);
- return !sync_prefs_.IsManaged() &&
- !sync_prefs_.IsStartSuppressed() &&
- prefs->GetString(prefs::kGoogleServicesUsername).empty();
+ // 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() {
@@ -259,6 +298,7 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
GetCredentialPathInCurrentProfile(),
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE));
+ local_store_->AddObserver(this);
local_store_->ReadPrefsAsync(NULL);
// Register for notifications for updates to the sync credentials, which are
@@ -267,11 +307,12 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this);
pref_registrar_.Add(prefs::kGoogleServicesUsername, this);
pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this);
- for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
- if (i == NIGORI) // The NIGORI preference is not persisted.
+ ModelTypeSet all_types = syncer::ModelTypeSet::All();
+ for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
+ if (it.Get() == NIGORI) // The NIGORI preference is not persisted.
continue;
pref_registrar_.Add(
- browser_sync::SyncPrefs::GetPrefNameForDataType(ModelTypeFromInt(i)),
+ browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()),
this);
}
@@ -289,13 +330,16 @@ void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
void CredentialCacheService::InitializeAlternateCredentialCacheReader(
bool* should_initialize) {
// If |should_initialize| is false, there was no credential cache in the
- // alternate profile directory, and there is nothing to do.
- // TODO(rsimha): Add a polling mechanism that periodically examines the
- // credential file in the alternate profile directory so we can respond to the
- // user signing in and signing out.
+ // alternate profile directory, and there is nothing to do right now. Schedule
+ // another read in the future and exit.
DCHECK(should_initialize);
- if (!*should_initialize)
+ if (!*should_initialize) {
+ ScheduleNextReadFromAlternateCredentialCache();
return;
+ }
+
+ // A credential cache file was found in the alternate profile. Prepare to
+ // consume its contents.
alternate_store_ = new JsonPrefStore(
GetCredentialPathInAlternateProfile(),
content::BrowserThread::GetMessageLoopProxyForThread(
@@ -316,18 +360,15 @@ bool CredentialCacheService::HasUserSignedOut() {
namespace {
-// Determines if credentials should be read from the alternate profile based
-// on the existence of the local and alternate credential files. Returns
-// true via |result| if there is a credential cache file in the alternate
-// profile, but there isn't one in the local profile. Returns false otherwise.
-void ShouldReadFromAlternateCache(
- const FilePath& credential_path_in_current_profile,
+// 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_current_profile) &&
- file_util::PathExists(credential_path_in_alternate_profile);
+ *result = file_util::PathExists(credential_path_in_alternate_profile);
}
} // namespace
@@ -337,8 +378,7 @@ void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
content::BrowserThread::PostTaskAndReply(
content::BrowserThread::FILE,
FROM_HERE,
- base::Bind(&ShouldReadFromAlternateCache,
- GetCredentialPathInCurrentProfile(),
+ base::Bind(&AlternateCredentialCacheExists,
GetCredentialPathInAlternateProfile(),
should_initialize),
base::Bind(
@@ -348,16 +388,30 @@ void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
}
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.";
+ 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 =
@@ -367,45 +421,78 @@ void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() {
std::string encryption_bootstrap_token =
GetAndUnpackStringPref(alternate_store_,
prefs::kSyncEncryptionBootstrapToken);
- bool keep_everything_synced =
- GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced);
- if (google_services_username.empty() ||
- lsid.empty() ||
- sid.empty() ||
- encryption_bootstrap_token.empty()) {
- VLOG(1) << "Found empty cached credentials.";
+ // 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;
}
- bool datatype_prefs[MODEL_TYPE_COUNT] = { false };
- for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
- if (i == NIGORI) // The NIGORI preference is not persisted.
- continue;
+ // 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(ModelTypeFromInt(i));
+ browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get());
if (!HasPref(alternate_store_, datatype_pref_name)) {
- VLOG(1) << "Could not find cached datatype prefs.";
- return;
+ // 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);
}
- datatype_prefs[i] = GetBooleanPref(alternate_store_, datatype_pref_name);
}
- ApplyCachedCredentials(google_services_username,
+ // Sign in if we notice new cached credentials in the alternate profile.
+ if (ShouldSignInToSync(google_services_username,
lsid,
sid,
- encryption_bootstrap_token,
- keep_everything_synced,
- datatype_prefs);
+ 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::ApplyCachedCredentials(
+void CredentialCacheService::InitiateSignInWithCachedCredentials(
const std::string& google_services_username,
- const std::string& lsid,
- const std::string& sid,
const std::string& encryption_bootstrap_token,
bool keep_everything_synced,
- const bool datatype_prefs[]) {
+ ModelTypeSet preferred_types) {
// Update the google username in the SigninManager and PrefStore.
ProfileSyncService* service =
ProfileSyncServiceFactory::GetForProfile(profile_);
@@ -418,19 +505,13 @@ void CredentialCacheService::ApplyCachedCredentials(
sync_prefs_.SetSyncSetupCompleted();
sync_prefs_.SetEncryptionBootstrapToken(encryption_bootstrap_token);
sync_prefs_.SetKeepEverythingSynced(keep_everything_synced);
- syncer::ModelTypeSet registered_types;
- syncer::ModelTypeSet preferred_types;
- for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
- if (i == NIGORI) // The NIGORI preference is not persisted.
- continue;
- registered_types.Put(ModelTypeFromInt(i));
- if (datatype_prefs[i])
- preferred_types.Put(ModelTypeFromInt(i));
- }
- sync_prefs_.SetPreferredDataTypes(registered_types, preferred_types);
+ sync_prefs_.SetPreferredDataTypes(service->GetRegisteredDataTypes(),
+ preferred_types);
+}
- // Update the lsid and sid in the TokenService and mint new tokens for all
- // Chrome services.
+void CredentialCacheService::UpdateTokenServiceCredentials(
+ const std::string& lsid,
+ const std::string& sid) {
GaiaAuthConsumer::ClientLoginResult login_result;
login_result.lsid = lsid;
login_result.sid = sid;
@@ -440,4 +521,107 @@ void CredentialCacheService::ApplyCachedCredentials(
token_service->StartFetchingTokens();
}
+void CredentialCacheService::InitiateSignOut() {
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ service->DisableForUser();
+}
+
+bool CredentialCacheService::HaveSyncPrefsChanged(
+ bool keep_everything_synced,
+ ModelTypeSet preferred_types) const {
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ ModelTypeSet local_preferred_types =
+ sync_prefs_.GetPreferredDataTypes(service->GetRegisteredDataTypes());
+ return
+ (keep_everything_synced != sync_prefs_.HasKeepEverythingSynced()) ||
+ !Difference(preferred_types, local_preferred_types).Empty();
+}
+
+bool CredentialCacheService::HaveTokenServiceCredentialsChanged(
+ const std::string& lsid,
+ const std::string& sid) {
+ std::string local_lsid =
+ GetAndUnpackStringPref(local_store_, GaiaConstants::kGaiaLsid);
+ std::string local_sid =
+ GetAndUnpackStringPref(local_store_, GaiaConstants::kGaiaSid);
+ return local_lsid != lsid || local_sid != sid;
+}
+
+bool CredentialCacheService::ShouldSignOutOfSync(
+ const std::string& 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() &&
+ google_services_username.empty() &&
+ !service->setup_in_progress() &&
+ (GetLastUpdatedTime(alternate_store_) >
+ GetLastUpdatedTime(local_store_));
+}
+
+bool CredentialCacheService::MayReconfigureSync(
+ const std::string& 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.
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ return !service->signin()->GetAuthenticatedUsername().empty() &&
+ !HasUserSignedOut() &&
+ (google_services_username ==
+ service->signin()->GetAuthenticatedUsername()) &&
+ !service->setup_in_progress() &&
+ (GetLastUpdatedTime(alternate_store_) >
+ GetLastUpdatedTime(local_store_));
+}
+
+bool CredentialCacheService::ShouldSignInToSync(
+ const std::string& google_services_username,
+ const std::string& lsid,
+ const std::string& sid,
+ const std::string& 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() &&
+ !google_services_username.empty() &&
+ !lsid.empty() &&
+ !sid.empty() &&
+ !encryption_bootstrap_token.empty() &&
+ !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
« no previous file with comments | « chrome/browser/sync/credential_cache_service_win.h ('k') | chrome/browser/sync/profile_sync_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698