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

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: Rebase Created 8 years, 5 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..13440a3951edf78ae27bd26d07bb9df84154d9b7 100644
--- a/chrome/browser/sync/credential_cache_service_win.cc
+++ b/chrome/browser/sync/credential_cache_service_win.cc
@@ -9,6 +9,7 @@
#include "base/base64.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
+#include "base/time.h"
#include "base/values.h"
#include "base/win/windows_version.h"
#include "chrome/browser/prefs/pref_service.h"
@@ -33,6 +34,12 @@
namespace syncer {
+// The time delay (in seconds) between two consecutive polls of the alternate
+// credential cache. A one 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 = 60;
+
+using base::TimeDelta;
using content::BrowserThread;
CredentialCacheService::CredentialCacheService(Profile* profile)
@@ -64,9 +71,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();
}
}
@@ -245,13 +257,7 @@ 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();
+ return !sync_prefs_.IsManaged() && !sync_prefs_.IsStartSuppressed();
Andrew T Wilson (Slow) 2012/08/03 20:39:56 Should we document why we want to look for credent
Raghu Simha 2012/08/03 21:57:35 Yes, it is to detect sign outs in the alternate pr
}
void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
@@ -259,6 +265,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 +274,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 +297,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 +327,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 +345,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 +355,28 @@ 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.
+ 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. Silently return as there is
+ // nothing to do.
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() << "\".";
return;
}
+ // Extract cached credentials from the alternate credential cache.
std::string google_services_username =
GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername);
std::string lsid =
@@ -367,45 +386,93 @@ 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 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 just notice that the user has signed out of the alternate profile.
+ // 4) The user is not already in the process of configuring sync.
+ // 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.
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ if (!sync_prefs_.GetGoogleServicesUsername().empty() &&
Andrew T Wilson (Slow) 2012/08/03 20:39:56 Let's change all these references to SigninManager
Raghu Simha 2012/08/03 21:57:35 Done.
+ !HasUserSignedOut() &&
+ google_services_username.empty() &&
+ !service->setup_in_progress()) {
Andrew T Wilson (Slow) 2012/08/03 20:39:56 Why do we want to avoid signing out the user if th
Raghu Simha 2012/08/03 21:57:35 If the user is configuring sync, we want to avoid
+ 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);
+ ModelTypeSet preferred_types;
+ 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(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());
Andrew T Wilson (Slow) 2012/08/03 20:39:56 Is this loop dangerous to execute if the user is c
Raghu Simha 2012/08/03 21:57:35 I don't think it's dangerous to run this loop. It'
+ }
+
+ // Reconfigure if sync settings or credentials have changed.
+ // Follow this up by scheduling a future read from the alternate 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);
+ ScheduleNextReadFromAlternateCredentialCache();
+ return;
}
- ApplyCachedCredentials(google_services_username,
- lsid,
- sid,
- encryption_bootstrap_token,
- keep_everything_synced,
- datatype_prefs);
+ // Try signing 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.
+ // Follow this up by scheduling a future read from the alternate profile.
+ if (sync_prefs_.GetGoogleServicesUsername().empty() &&
+ !HasUserSignedOut() &&
+ !google_services_username.empty() &&
+ !lsid.empty() &&
+ !sid.empty() &&
+ !encryption_bootstrap_token.empty() &&
+ !service->setup_in_progress()) {
+ InitiateSignInWithCachedCredentials(google_services_username,
+ encryption_bootstrap_token,
+ keep_everything_synced,
+ preferred_types);
+ UpdateTokenServiceCredentials(lsid, sid);
+ ScheduleNextReadFromAlternateCredentialCache();
+ return;
+ }
}
-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 +485,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 +501,63 @@ void CredentialCacheService::ApplyCachedCredentials(
token_service->StartFetchingTokens();
}
+void CredentialCacheService::InitiateSignOut() {
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ service->DisableForUser();
Andrew T Wilson (Slow) 2012/08/03 20:39:56 This is fine, but it's slightly better to use Sign
Raghu Simha 2012/08/03 21:57:35 Done.
Raghu Simha 2012/08/03 22:12:52 Actually, PSS doesn't handle the notification sent
+}
+
+bool CredentialCacheService::HaveSyncPrefsChanged(
+ bool keep_everything_synced,
+ const 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::MayReconfigureSync(
+ const std::string& google_services_username) {
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ // 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.
Andrew T Wilson (Slow) 2012/08/03 20:39:56 You left out step 4: Profit
Raghu Simha 2012/08/03 21:57:35 Hah! Fixed.
+ // 5) The user is not already in the process of configuring sync.
+ return !sync_prefs_.GetGoogleServicesUsername().empty() &&
+ !HasUserSignedOut() &&
+ google_services_username == sync_prefs_.GetGoogleServicesUsername() &&
+ !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();
+ }
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &CredentialCacheService::LookForCachedCredentialsInAlternateProfile,
+ weak_factory_.GetWeakPtr()),
+ 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