Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/credential_cache_service_win.h" | 5 #include "chrome/browser/sync/credential_cache_service_win.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "base/string_number_conversions.h" | 12 #include "base/string_number_conversions.h" |
| 13 #include "base/time.h" | 13 #include "base/time.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 #include "base/win/windows_version.h" | 15 #include "base/win/windows_version.h" |
| 16 #include "chrome/browser/prefs/pref_service.h" | 16 #include "chrome/browser/prefs/pref_service.h" |
| 17 #include "chrome/browser/profiles/profile.h" | 17 #include "chrome/browser/profiles/profile.h" |
| 18 #include "chrome/browser/profiles/profile_manager.h" | 18 #include "chrome/browser/profiles/profile_manager.h" |
| 19 #include "chrome/browser/signin/signin_manager.h" | 19 #include "chrome/browser/signin/signin_manager.h" |
| 20 #include "chrome/browser/signin/signin_manager_factory.h" | |
| 20 #include "chrome/browser/signin/token_service.h" | 21 #include "chrome/browser/signin/token_service.h" |
| 21 #include "chrome/browser/signin/token_service_factory.h" | 22 #include "chrome/browser/signin/token_service_factory.h" |
| 22 #include "chrome/browser/sync/glue/chrome_encryptor.h" | 23 #include "chrome/browser/sync/glue/chrome_encryptor.h" |
| 23 #include "chrome/browser/sync/profile_sync_service.h" | 24 #include "chrome/browser/sync/profile_sync_service.h" |
| 24 #include "chrome/browser/sync/profile_sync_service_factory.h" | 25 #include "chrome/browser/sync/profile_sync_service_factory.h" |
| 25 #include "chrome/common/chrome_constants.h" | 26 #include "chrome/common/chrome_constants.h" |
| 26 #include "chrome/common/chrome_notification_types.h" | 27 #include "chrome/common/chrome_notification_types.h" |
| 27 #include "chrome/common/chrome_paths_internal.h" | 28 #include "chrome/common/chrome_paths_internal.h" |
| 28 #include "chrome/common/net/gaia/gaia_auth_consumer.h" | 29 #include "chrome/common/net/gaia/gaia_auth_consumer.h" |
| 29 #include "chrome/common/net/gaia/gaia_constants.h" | 30 #include "chrome/common/net/gaia/gaia_constants.h" |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 48 using base::TimeDelta; | 49 using base::TimeDelta; |
| 49 using content::BrowserThread; | 50 using content::BrowserThread; |
| 50 | 51 |
| 51 CredentialCacheService::CredentialCacheService(Profile* profile) | 52 CredentialCacheService::CredentialCacheService(Profile* profile) |
| 52 : profile_(profile), | 53 : profile_(profile), |
| 53 // |profile_| is null in unit tests. | 54 // |profile_| is null in unit tests. |
| 54 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), | 55 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), |
| 55 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 56 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 56 if (profile_) { | 57 if (profile_) { |
| 57 InitializeLocalCredentialCacheWriter(); | 58 InitializeLocalCredentialCacheWriter(); |
| 58 if (ShouldLookForCachedCredentialsInAlternateProfile()) | 59 // If sync is not disabled, look for credentials in the alternate profile. |
| 60 // Note that we do want to look for credentials in the alternate profile | |
| 61 // even if the local user is signed in, so that we can detect a sign out or | |
| 62 // reconfigure originating from the alternate profile. | |
| 63 if (!sync_prefs_.IsManaged()) | |
| 59 LookForCachedCredentialsInAlternateProfile(); | 64 LookForCachedCredentialsInAlternateProfile(); |
| 60 } | 65 } |
| 61 } | 66 } |
| 62 | 67 |
| 63 CredentialCacheService::~CredentialCacheService() { | 68 CredentialCacheService::~CredentialCacheService() { |
| 64 Shutdown(); | 69 Shutdown(); |
| 65 } | 70 } |
| 66 | 71 |
| 67 void CredentialCacheService::Shutdown() { | 72 void CredentialCacheService::Shutdown() { |
| 68 if (local_store_.get()) { | 73 if (local_store_.get()) { |
| 74 local_store_observer_.release(); | |
| 69 local_store_.release(); | 75 local_store_.release(); |
| 70 } | 76 } |
| 71 | 77 |
| 72 if (alternate_store_.get()) { | 78 if (alternate_store_.get()) { |
| 73 alternate_store_->RemoveObserver(this); | 79 alternate_store_observer_.release(); |
| 74 alternate_store_.release(); | 80 alternate_store_.release(); |
| 75 } | 81 } |
| 76 } | 82 } |
| 77 | 83 |
| 78 void CredentialCacheService::OnInitializationCompleted(bool succeeded) { | |
| 79 DCHECK(succeeded); | |
| 80 // When the local and alternate credential stores become available, begin | |
| 81 // consuming the alternate cached credentials. We must also wait for the local | |
| 82 // credential store because the credentials read from the alternate cache and | |
| 83 // applied locally must eventually get stored in the local cache. | |
| 84 if (alternate_store_.get() && | |
| 85 alternate_store_->IsInitializationComplete() && | |
| 86 local_store_.get() && | |
| 87 local_store_->IsInitializationComplete()) { | |
| 88 ReadCachedCredentialsFromAlternateProfile(); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 void CredentialCacheService::OnPrefValueChanged(const std::string& key) { | |
| 93 // Nothing to do here, since credentials are cached silently. | |
| 94 } | |
| 95 | |
| 96 void CredentialCacheService::Observe( | 84 void CredentialCacheService::Observe( |
| 97 int type, | 85 int type, |
| 98 const content::NotificationSource& source, | 86 const content::NotificationSource& source, |
| 99 const content::NotificationDetails& details) { | 87 const content::NotificationDetails& details) { |
| 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 101 DCHECK(local_store_.get()); | 89 DCHECK(local_store_.get()); |
| 102 switch (type) { | 90 switch (type) { |
| 103 case chrome::NOTIFICATION_PREF_CHANGED: { | 91 case chrome::NOTIFICATION_PREF_CHANGED: { |
| 104 const std::string pref_name = | 92 const std::string pref_name = |
| 105 *(content::Details<const std::string>(details).ptr()); | 93 *(content::Details<const std::string>(details).ptr()); |
| 106 if (pref_name == prefs::kGoogleServicesUsername || | 94 if (pref_name == prefs::kGoogleServicesUsername) { |
| 107 pref_name == prefs::kSyncEncryptionBootstrapToken) { | 95 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); |
| 108 PackAndUpdateStringPref( | 96 PackAndUpdateStringPref(pref_name, signin->GetAuthenticatedUsername()); |
| 109 pref_name, | 97 break; |
| 110 profile_->GetPrefs()->GetString(pref_name.c_str())); | |
| 111 } else { | |
| 112 UpdateBooleanPref(pref_name, | |
| 113 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); | |
| 114 } | 98 } |
| 99 if (pref_name == prefs::kSyncEncryptionBootstrapToken) { | |
| 100 PackAndUpdateStringPref(pref_name, | |
| 101 sync_prefs_.GetEncryptionBootstrapToken()); | |
| 102 break; | |
| 103 } | |
| 104 UpdateBooleanPref(pref_name, | |
| 105 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); | |
| 115 break; | 106 break; |
| 116 } | 107 } |
| 117 | 108 |
| 118 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { | 109 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { |
| 119 const TokenService::CredentialsUpdatedDetails& token_details = | 110 const TokenService::CredentialsUpdatedDetails& token_details = |
| 120 *(content::Details<const TokenService::CredentialsUpdatedDetails>( | 111 *(content::Details<const TokenService::CredentialsUpdatedDetails>( |
| 121 details).ptr()); | 112 details).ptr()); |
| 122 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); | 113 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); |
| 123 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); | 114 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); |
| 124 break; | 115 break; |
| 125 } | 116 } |
| 126 | 117 |
| 127 case chrome::NOTIFICATION_TOKENS_CLEARED: { | 118 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
| 128 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); | 119 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); |
| 129 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); | 120 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); |
| 130 break; | 121 break; |
| 131 } | 122 } |
| 132 | 123 |
| 124 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { | |
| 125 // If there is no existing local credential cache, and the token service | |
| 126 // already has valid credentials as a result of the user having signed in, | |
| 127 // write them to the cache. Used in cases where the user was already | |
| 128 // signed in and then upgraded from a version of chrome that didn't | |
| 129 // support credential caching. | |
| 130 if (local_store_.get() && | |
| 131 local_store_->IsInitializationComplete() && | |
| 132 local_store_->GetReadError() == | |
| 133 JsonPrefStore::PREF_READ_ERROR_NO_FILE) { | |
| 134 TokenService* token_service = | |
| 135 TokenServiceFactory::GetForProfile(profile_); | |
| 136 if (token_service->AreCredentialsValid()) { | |
| 137 GaiaAuthConsumer::ClientLoginResult credentials = | |
| 138 token_service->credentials(); | |
| 139 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, credentials.lsid); | |
| 140 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, credentials.sid); | |
| 141 } | |
| 142 } | |
| 143 break; | |
| 144 } | |
| 145 | |
| 133 default: { | 146 default: { |
| 134 NOTREACHED(); | 147 NOTREACHED(); |
| 135 break; | 148 break; |
| 136 } | 149 } |
| 137 } | 150 } |
| 138 } | 151 } |
| 139 | 152 |
| 153 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() { | |
|
Raghu Simha
2012/08/09 06:10:40
ReadCachedCredentialsFromAlternateProfile() was me
| |
| 154 // If the local user has signed in and signed out, we do not consume cached | |
| 155 // credentials from the alternate profile. There is nothing more to do, now or | |
| 156 // later on. | |
| 157 if (HasUserSignedOut()) | |
| 158 return; | |
| 159 | |
| 160 // Sanity check the alternate credential cache. If any string credentials | |
| 161 // are outright missing even though the file exists, something is awry with | |
| 162 // the alternate profile store. There is no sense in flagging an error as the | |
| 163 // problem lies in a different profile directory. There is nothing to do now. | |
| 164 // We schedule a future read from the alternate credential cache and return. | |
| 165 DCHECK(alternate_store_.get()); | |
| 166 if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) || | |
| 167 !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) || | |
| 168 !HasPref(alternate_store_, GaiaConstants::kGaiaSid) || | |
| 169 !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) || | |
| 170 !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { | |
| 171 VLOG(1) << "Could not find cached credentials in \"" | |
| 172 << GetCredentialPathInAlternateProfile().value() << "\"."; | |
| 173 ScheduleNextReadFromAlternateCredentialCache(); | |
| 174 return; | |
| 175 } | |
| 176 | |
| 177 // Extract cached credentials from the alternate credential cache. | |
| 178 std::string google_services_username = | |
| 179 GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); | |
| 180 std::string lsid = | |
| 181 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); | |
| 182 std::string sid = | |
| 183 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); | |
| 184 std::string encryption_bootstrap_token = | |
| 185 GetAndUnpackStringPref(alternate_store_, | |
| 186 prefs::kSyncEncryptionBootstrapToken); | |
| 187 | |
| 188 // Sign out of sync if the alternate profile has signed out the same user. | |
| 189 // There is no need to schedule any more reads of the alternate profile | |
| 190 // cache because we only apply cached credentials for first-time sign-ins. | |
| 191 if (ShouldSignOutOfSync(google_services_username)) { | |
| 192 VLOG(1) << "User has signed out on the other profile. Signing out."; | |
| 193 InitiateSignOut(); | |
| 194 return; | |
| 195 } | |
| 196 | |
| 197 // Extract cached sync prefs from the alternate credential cache. | |
| 198 bool keep_everything_synced = | |
| 199 GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); | |
| 200 ProfileSyncService* service = | |
| 201 ProfileSyncServiceFactory::GetForProfile(profile_); | |
| 202 ModelTypeSet registered_types = service->GetRegisteredDataTypes(); | |
| 203 ModelTypeSet preferred_types; | |
| 204 for (ModelTypeSet::Iterator it = registered_types.First(); | |
| 205 it.Good(); | |
| 206 it.Inc()) { | |
| 207 std::string datatype_pref_name = | |
| 208 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); | |
| 209 if (!HasPref(alternate_store_, datatype_pref_name)) { | |
| 210 // If there is no cached pref for a specific data type, it means that the | |
| 211 // user originally signed in with an older version of Chrome, and then | |
| 212 // upgraded to a version with a new datatype. In such cases, we leave the | |
| 213 // default initial datatype setting as false while reading cached | |
| 214 // credentials, just like we do in SyncPrefs::RegisterPreferences. | |
| 215 VLOG(1) << "Could not find cached datatype pref for " | |
| 216 << datatype_pref_name << " in " | |
| 217 << GetCredentialPathInAlternateProfile().value() << "."; | |
| 218 continue; | |
| 219 } | |
| 220 if (GetBooleanPref(alternate_store_, datatype_pref_name)) | |
| 221 preferred_types.Put(it.Get()); | |
| 222 } | |
| 223 | |
| 224 // Reconfigure if sync settings or credentials have changed in the alternate | |
| 225 // profile, but for the same user that is signed in to the local profile. | |
| 226 if (MayReconfigureSync(google_services_username)) { | |
| 227 if (HaveSyncPrefsChanged(keep_everything_synced, preferred_types)) { | |
| 228 VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; | |
| 229 service->OnUserChoseDatatypes(keep_everything_synced, preferred_types); | |
| 230 } | |
| 231 if (HaveTokenServiceCredentialsChanged(lsid, sid)) { | |
| 232 VLOG(1) << "Token service credentials have changed in other profile."; | |
| 233 UpdateTokenServiceCredentials(lsid, sid); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 // Sign in if we notice new cached credentials in the alternate profile. | |
| 238 if (ShouldSignInToSync(google_services_username, | |
| 239 lsid, | |
| 240 sid, | |
| 241 encryption_bootstrap_token)) { | |
| 242 InitiateSignInWithCachedCredentials(google_services_username, | |
| 243 encryption_bootstrap_token, | |
| 244 keep_everything_synced, | |
| 245 preferred_types); | |
| 246 UpdateTokenServiceCredentials(lsid, sid); | |
| 247 } | |
| 248 | |
| 249 // Schedule the next read from the alternate credential cache so that we can | |
| 250 // detect future reconfigures or sign outs. | |
| 251 ScheduleNextReadFromAlternateCredentialCache(); | |
| 252 } | |
| 253 | |
| 254 void CredentialCacheService::WriteExistingSyncPrefsToLocalCache() { | |
| 255 // If the local user is already signed in and there is no local credential | |
| 256 // cache file, write all the existing sync prefs to the local cache. | |
| 257 DCHECK(local_store_.get() && | |
| 258 local_store_->GetReadError() == | |
| 259 JsonPrefStore::PREF_READ_ERROR_NO_FILE); | |
| 260 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); | |
| 261 if (!signin->GetAuthenticatedUsername().empty() && | |
| 262 !HasPref(local_store_, prefs::kGoogleServicesUsername)) { | |
| 263 PackAndUpdateStringPref(prefs::kGoogleServicesUsername, | |
| 264 signin->GetAuthenticatedUsername()); | |
| 265 PackAndUpdateStringPref(prefs::kSyncEncryptionBootstrapToken, | |
| 266 sync_prefs_.GetEncryptionBootstrapToken()); | |
| 267 UpdateBooleanPref(prefs::kSyncKeepEverythingSynced, | |
| 268 sync_prefs_.HasKeepEverythingSynced()); | |
| 269 ProfileSyncService* service = | |
| 270 ProfileSyncServiceFactory::GetForProfile(profile_); | |
| 271 ModelTypeSet registered_types = service->GetRegisteredDataTypes(); | |
| 272 for (ModelTypeSet::Iterator it = registered_types.First(); | |
| 273 it.Good(); | |
| 274 it.Inc()) { | |
| 275 std::string datatype_pref_name = | |
| 276 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); | |
| 277 UpdateBooleanPref( | |
| 278 datatype_pref_name, | |
| 279 profile_->GetPrefs()->GetBoolean(datatype_pref_name.c_str())); | |
| 280 } | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache() { | |
| 285 // We must reinitialize |alternate_store_| here because the underlying | |
| 286 // credential file in the alternate profile might have changed, and we must | |
| 287 // re-read it afresh. | |
| 288 if (alternate_store_.get()) { | |
| 289 alternate_store_observer_.release(); | |
| 290 alternate_store_.release(); | |
| 291 } | |
| 292 next_read_.Reset(base::Bind( | |
| 293 &CredentialCacheService::LookForCachedCredentialsInAlternateProfile, | |
| 294 weak_factory_.GetWeakPtr())); | |
| 295 MessageLoop::current()->PostDelayedTask( | |
| 296 FROM_HERE, | |
| 297 next_read_.callback(), | |
| 298 TimeDelta::FromSeconds(kCredentialCachePollIntervalSecs)); | |
| 299 } | |
| 300 | |
| 140 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, | 301 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, |
| 141 const std::string& pref_name) { | 302 const std::string& pref_name) { |
| 142 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); | 303 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); |
| 143 } | 304 } |
| 144 | 305 |
| 145 // static | 306 // static |
| 146 base::StringValue* CredentialCacheService::PackCredential( | 307 base::StringValue* CredentialCacheService::PackCredential( |
| 147 const std::string& credential) { | 308 const std::string& credential) { |
| 148 // Do nothing for empty credentials. | 309 // Do nothing for empty credentials. |
| 149 if (credential.empty()) | 310 if (credential.empty()) |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 bool CredentialCacheService::GetBooleanPref( | 417 bool CredentialCacheService::GetBooleanPref( |
| 257 scoped_refptr<JsonPrefStore> store, | 418 scoped_refptr<JsonPrefStore> store, |
| 258 const std::string& pref_name) { | 419 const std::string& pref_name) { |
| 259 const base::Value* pref_value = NULL; | 420 const base::Value* pref_value = NULL; |
| 260 store->GetValue(pref_name, &pref_value); | 421 store->GetValue(pref_name, &pref_value); |
| 261 bool pref; | 422 bool pref; |
| 262 pref_value->GetAsBoolean(&pref); | 423 pref_value->GetAsBoolean(&pref); |
| 263 return pref; | 424 return pref; |
| 264 } | 425 } |
| 265 | 426 |
| 427 CredentialCacheService::LocalStoreObserver::LocalStoreObserver( | |
| 428 CredentialCacheService* service, | |
| 429 scoped_refptr<JsonPrefStore> local_store) | |
| 430 : service_(service), | |
| 431 local_store_(local_store) { | |
| 432 local_store_->AddObserver(this); | |
| 433 } | |
| 434 | |
| 435 CredentialCacheService::LocalStoreObserver::~LocalStoreObserver() { | |
| 436 local_store_->RemoveObserver(this); | |
| 437 } | |
| 438 | |
| 439 void CredentialCacheService::LocalStoreObserver::OnInitializationCompleted( | |
| 440 bool succeeded) { | |
| 441 // If there is no existing local credential cache, write any existing sync | |
| 442 // prefs to the local cache. This could happen if the user was already signed | |
| 443 // in and restarts chrome after upgrading from an older version that didn't | |
| 444 // support credential caching. Note that |succeeded| will be true even if | |
| 445 // the local cache file wasn't found, so long as its parent dir was found. | |
| 446 DCHECK(succeeded); | |
| 447 if (local_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NO_FILE) { | |
| 448 service_->WriteExistingSyncPrefsToLocalCache(); | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 void CredentialCacheService::LocalStoreObserver::OnPrefValueChanged( | |
| 453 const std::string& key) { | |
| 454 // Nothing to do here, since credentials are cached silently. | |
| 455 } | |
| 456 | |
| 457 CredentialCacheService::AlternateStoreObserver::AlternateStoreObserver( | |
| 458 CredentialCacheService* service, | |
| 459 scoped_refptr<JsonPrefStore> alternate_store) | |
| 460 : service_(service), | |
| 461 alternate_store_(alternate_store) { | |
| 462 alternate_store_->AddObserver(this); | |
| 463 } | |
| 464 | |
| 465 CredentialCacheService::AlternateStoreObserver::~AlternateStoreObserver() { | |
| 466 alternate_store_->RemoveObserver(this); | |
| 467 } | |
| 468 | |
| 469 void CredentialCacheService::AlternateStoreObserver::OnInitializationCompleted( | |
| 470 bool succeeded) { | |
| 471 // If an alternate credential cache was found, begin consuming its contents. | |
| 472 // If not, schedule a future read. | |
| 473 if (succeeded && | |
| 474 alternate_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE) { | |
| 475 service_->ReadCachedCredentialsFromAlternateProfile(); | |
| 476 } else { | |
| 477 service_->ScheduleNextReadFromAlternateCredentialCache(); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 void CredentialCacheService::AlternateStoreObserver::OnPrefValueChanged( | |
| 482 const std::string& key) { | |
| 483 // Nothing to do here, since credentials are cached silently. | |
| 484 } | |
| 485 | |
| 266 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { | 486 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { |
| 267 // The sync credential path in the default Desktop profile is | 487 // The sync credential path in the default Desktop profile is |
| 268 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while | 488 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while |
| 269 // the sync credential path in the default Metro profile is | 489 // the sync credential path in the default Metro profile is |
| 270 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". | 490 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". |
| 271 DCHECK(profile_); | 491 DCHECK(profile_); |
| 272 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); | 492 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); |
| 273 } | 493 } |
| 274 | 494 |
| 275 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { | 495 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { |
| 276 DCHECK(profile_); | 496 DCHECK(profile_); |
| 277 FilePath alternate_user_data_dir; | 497 FilePath alternate_user_data_dir; |
| 278 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); | 498 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); |
| 279 FilePath alternate_default_profile_dir = | 499 FilePath alternate_default_profile_dir = |
| 280 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); | 500 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); |
| 281 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); | 501 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); |
| 282 } | 502 } |
| 283 | 503 |
| 284 bool CredentialCacheService::ShouldLookForCachedCredentialsInAlternateProfile() | |
| 285 const { | |
| 286 // We must look for credentials in the alternate profile iff the following are | |
| 287 // true: | |
| 288 // 1) Sync is not disabled by policy. | |
| 289 // 2) Sync startup is not suppressed. | |
| 290 // Note that we do want to look for credentials in the alternate profile even | |
| 291 // if the local user is signed in, so we can detect a sign out originating | |
| 292 // from the alternate profile. | |
| 293 return !sync_prefs_.IsManaged() && !sync_prefs_.IsStartSuppressed(); | |
| 294 } | |
| 295 | |
| 296 void CredentialCacheService::InitializeLocalCredentialCacheWriter() { | 504 void CredentialCacheService::InitializeLocalCredentialCacheWriter() { |
| 297 local_store_ = new JsonPrefStore( | 505 local_store_ = new JsonPrefStore( |
| 298 GetCredentialPathInCurrentProfile(), | 506 GetCredentialPathInCurrentProfile(), |
| 299 content::BrowserThread::GetMessageLoopProxyForThread( | 507 content::BrowserThread::GetMessageLoopProxyForThread( |
| 300 content::BrowserThread::FILE)); | 508 content::BrowserThread::FILE)); |
| 301 local_store_->AddObserver(this); | 509 local_store_observer_ = new LocalStoreObserver(this, local_store_); |
| 302 local_store_->ReadPrefsAsync(NULL); | 510 local_store_->ReadPrefsAsync(NULL); |
| 303 | 511 |
| 304 // Register for notifications for updates to the sync credentials, which are | 512 // Register for notifications for updates to the sync credentials, which are |
| 305 // stored in the PrefStore. | 513 // stored in the PrefStore. |
| 306 pref_registrar_.Init(profile_->GetPrefs()); | 514 pref_registrar_.Init(profile_->GetPrefs()); |
| 307 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); | 515 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); |
| 308 pref_registrar_.Add(prefs::kGoogleServicesUsername, this); | 516 pref_registrar_.Add(prefs::kGoogleServicesUsername, this); |
| 309 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); | 517 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); |
| 310 ModelTypeSet all_types = syncer::ModelTypeSet::All(); | 518 ModelTypeSet all_types = syncer::ModelTypeSet::All(); |
| 311 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | 519 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { |
| 312 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. | 520 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. |
| 313 continue; | 521 continue; |
| 314 pref_registrar_.Add( | 522 pref_registrar_.Add( |
| 315 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), | 523 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), |
| 316 this); | 524 this); |
| 317 } | 525 } |
| 318 | 526 |
| 319 // Register for notifications for updates to lsid and sid, which are stored in | 527 // Register for notifications for updates to lsid and sid, which are stored in |
| 320 // the TokenService. | 528 // the TokenService. |
| 321 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); | 529 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
| 322 registrar_.Add(this, | 530 registrar_.Add(this, |
| 323 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, | 531 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, |
| 324 content::Source<TokenService>(token_service)); | 532 content::Source<TokenService>(token_service)); |
| 325 registrar_.Add(this, | 533 registrar_.Add(this, |
| 326 chrome::NOTIFICATION_TOKENS_CLEARED, | 534 chrome::NOTIFICATION_TOKENS_CLEARED, |
| 327 content::Source<TokenService>(token_service)); | 535 content::Source<TokenService>(token_service)); |
| 536 registrar_.Add(this, | |
| 537 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, | |
| 538 content::Source<TokenService>(token_service)); | |
| 328 } | 539 } |
| 329 | 540 |
| 330 void CredentialCacheService::InitializeAlternateCredentialCacheReader( | 541 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { |
| 331 bool* should_initialize) { | 542 // Attempt to read cached credentials from the alternate profile. If no file |
| 332 // If |should_initialize| is false, there was no credential cache in the | 543 // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned |
| 333 // alternate profile directory, and there is nothing to do right now. Schedule | 544 // after initialization is complete. |
| 334 // another read in the future and exit. | |
| 335 DCHECK(should_initialize); | |
| 336 if (!*should_initialize) { | |
| 337 ScheduleNextReadFromAlternateCredentialCache(); | |
| 338 return; | |
| 339 } | |
| 340 | |
| 341 // A credential cache file was found in the alternate profile. Prepare to | |
| 342 // consume its contents. | |
| 343 alternate_store_ = new JsonPrefStore( | 545 alternate_store_ = new JsonPrefStore( |
| 344 GetCredentialPathInAlternateProfile(), | 546 GetCredentialPathInAlternateProfile(), |
| 345 content::BrowserThread::GetMessageLoopProxyForThread( | 547 content::BrowserThread::GetMessageLoopProxyForThread( |
| 346 content::BrowserThread::FILE)); | 548 content::BrowserThread::FILE)); |
| 347 alternate_store_->AddObserver(this); | 549 alternate_store_observer_ = new AlternateStoreObserver(this, |
| 550 alternate_store_); | |
| 348 alternate_store_->ReadPrefsAsync(NULL); | 551 alternate_store_->ReadPrefsAsync(NULL); |
| 349 } | 552 } |
| 350 | 553 |
| 351 bool CredentialCacheService::HasUserSignedOut() { | 554 bool CredentialCacheService::HasUserSignedOut() { |
| 352 DCHECK(local_store_.get()); | 555 DCHECK(local_store_.get()); |
| 353 // If HasPref() is false, the user never signed in, since there are no | 556 // If HasPref() is false, the user never signed in, since there are no |
| 354 // previously cached credentials. If the kGoogleServicesUsername pref is | 557 // previously cached credentials. If the kGoogleServicesUsername pref is |
| 355 // empty, it means that the user signed in and subsequently signed out. | 558 // empty, it means that the user signed in and subsequently signed out. |
| 356 return HasPref(local_store_, prefs::kGoogleServicesUsername) && | 559 return HasPref(local_store_, prefs::kGoogleServicesUsername) && |
| 357 GetAndUnpackStringPref(local_store_, | 560 GetAndUnpackStringPref(local_store_, |
| 358 prefs::kGoogleServicesUsername).empty(); | 561 prefs::kGoogleServicesUsername).empty(); |
| 359 } | 562 } |
| 360 | 563 |
| 361 namespace { | |
| 362 | |
| 363 // Determines if there is a sync credential cache in the alternate profile. | |
| 364 // Returns true via |result| if there is a credential cache file in the | |
| 365 // alternate profile. Returns false otherwise. | |
| 366 void AlternateCredentialCacheExists( | |
| 367 const FilePath& credential_path_in_alternate_profile, | |
| 368 bool* result) { | |
| 369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
| 370 DCHECK(result); | |
| 371 *result = file_util::PathExists(credential_path_in_alternate_profile); | |
| 372 } | |
| 373 | |
| 374 } // namespace | |
| 375 | |
| 376 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { | |
| 377 bool* should_initialize = new bool(false); | |
| 378 content::BrowserThread::PostTaskAndReply( | |
| 379 content::BrowserThread::FILE, | |
| 380 FROM_HERE, | |
| 381 base::Bind(&AlternateCredentialCacheExists, | |
| 382 GetCredentialPathInAlternateProfile(), | |
| 383 should_initialize), | |
| 384 base::Bind( | |
| 385 &CredentialCacheService::InitializeAlternateCredentialCacheReader, | |
| 386 weak_factory_.GetWeakPtr(), | |
| 387 base::Owned(should_initialize))); | |
| 388 } | |
| 389 | |
| 390 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() { | |
| 391 // If the local user has signed in and signed out, we do not consume cached | |
| 392 // credentials from the alternate profile. There is nothing more to do, now or | |
| 393 // later on. | |
| 394 if (HasUserSignedOut()) | |
| 395 return; | |
| 396 | |
| 397 // Sanity check the alternate credential cache. If any string credentials | |
| 398 // are outright missing even though the file exists, something is awry with | |
| 399 // the alternate profile store. There is no sense in flagging an error as the | |
| 400 // problem lies in a different profile directory. There is nothing to do now. | |
| 401 // We schedule a future read from the alternate credential cache and return. | |
| 402 DCHECK(alternate_store_.get()); | |
| 403 if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) || | |
| 404 !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) || | |
| 405 !HasPref(alternate_store_, GaiaConstants::kGaiaSid) || | |
| 406 !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) || | |
| 407 !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { | |
| 408 VLOG(1) << "Could not find cached credentials in \"" | |
| 409 << GetCredentialPathInAlternateProfile().value() << "\"."; | |
| 410 ScheduleNextReadFromAlternateCredentialCache(); | |
| 411 return; | |
| 412 } | |
| 413 | |
| 414 // Extract cached credentials from the alternate credential cache. | |
| 415 std::string google_services_username = | |
| 416 GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); | |
| 417 std::string lsid = | |
| 418 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); | |
| 419 std::string sid = | |
| 420 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); | |
| 421 std::string encryption_bootstrap_token = | |
| 422 GetAndUnpackStringPref(alternate_store_, | |
| 423 prefs::kSyncEncryptionBootstrapToken); | |
| 424 | |
| 425 // Sign out of sync if the alternate profile has signed out the same user. | |
| 426 // There is no need to schedule any more reads of the alternate profile | |
| 427 // cache because we only apply cached credentials for first-time sign-ins. | |
| 428 if (ShouldSignOutOfSync(google_services_username)) { | |
| 429 VLOG(1) << "User has signed out on the other profile. Signing out."; | |
| 430 InitiateSignOut(); | |
| 431 return; | |
| 432 } | |
| 433 | |
| 434 // Extract cached sync prefs from the alternate credential cache. | |
| 435 bool keep_everything_synced = | |
| 436 GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); | |
| 437 ProfileSyncService* service = | |
| 438 ProfileSyncServiceFactory::GetForProfile(profile_); | |
| 439 ModelTypeSet registered_types = service->GetRegisteredDataTypes(); | |
| 440 ModelTypeSet preferred_types; | |
| 441 for (ModelTypeSet::Iterator it = registered_types.First(); | |
| 442 it.Good(); | |
| 443 it.Inc()) { | |
| 444 std::string datatype_pref_name = | |
| 445 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); | |
| 446 if (!HasPref(alternate_store_, datatype_pref_name)) { | |
| 447 // If there is no cached pref for a specific data type, it means that the | |
| 448 // user originally signed in with an older version of Chrome, and then | |
| 449 // upgraded to a version with a new datatype. In such cases, we leave the | |
| 450 // default initial datatype setting as false while reading cached | |
| 451 // credentials, just like we do in SyncPrefs::RegisterPreferences. | |
| 452 VLOG(1) << "Could not find cached datatype pref for " | |
| 453 << datatype_pref_name << " in " | |
| 454 << GetCredentialPathInAlternateProfile().value() << "."; | |
| 455 continue; | |
| 456 } | |
| 457 if (GetBooleanPref(alternate_store_, datatype_pref_name)) | |
| 458 preferred_types.Put(it.Get()); | |
| 459 } | |
| 460 | |
| 461 // Reconfigure if sync settings or credentials have changed in the alternate | |
| 462 // profile, but for the same user that is signed in to the local profile. | |
| 463 if (MayReconfigureSync(google_services_username)) { | |
| 464 if (HaveSyncPrefsChanged(keep_everything_synced, preferred_types)) { | |
| 465 VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; | |
| 466 service->OnUserChoseDatatypes(keep_everything_synced, preferred_types); | |
| 467 } | |
| 468 if (HaveTokenServiceCredentialsChanged(lsid, sid)) { | |
| 469 VLOG(1) << "Token service credentials have changed in other profile."; | |
| 470 UpdateTokenServiceCredentials(lsid, sid); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 // Sign in if we notice new cached credentials in the alternate profile. | |
| 475 if (ShouldSignInToSync(google_services_username, | |
| 476 lsid, | |
| 477 sid, | |
| 478 encryption_bootstrap_token)) { | |
| 479 InitiateSignInWithCachedCredentials(google_services_username, | |
| 480 encryption_bootstrap_token, | |
| 481 keep_everything_synced, | |
| 482 preferred_types); | |
| 483 UpdateTokenServiceCredentials(lsid, sid); | |
| 484 } | |
| 485 | |
| 486 // Schedule the next read from the alternate credential cache so that we can | |
| 487 // detect future reconfigures or sign outs. | |
| 488 ScheduleNextReadFromAlternateCredentialCache(); | |
| 489 } | |
| 490 | |
| 491 void CredentialCacheService::InitiateSignInWithCachedCredentials( | 564 void CredentialCacheService::InitiateSignInWithCachedCredentials( |
| 492 const std::string& google_services_username, | 565 const std::string& google_services_username, |
| 493 const std::string& encryption_bootstrap_token, | 566 const std::string& encryption_bootstrap_token, |
| 494 bool keep_everything_synced, | 567 bool keep_everything_synced, |
| 495 ModelTypeSet preferred_types) { | 568 ModelTypeSet preferred_types) { |
| 496 // Update the google username in the SigninManager and PrefStore. | 569 // Update the google username in the SigninManager and PrefStore. |
| 497 ProfileSyncService* service = | 570 ProfileSyncService* service = |
| 498 ProfileSyncServiceFactory::GetForProfile(profile_); | 571 ProfileSyncServiceFactory::GetForProfile(profile_); |
| 499 service->signin()->SetAuthenticatedUsername(google_services_username); | 572 service->signin()->SetAuthenticatedUsername(google_services_username); |
| 500 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, | 573 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 600 ProfileSyncServiceFactory::GetForProfile(profile_); | 673 ProfileSyncServiceFactory::GetForProfile(profile_); |
| 601 return service->signin()->GetAuthenticatedUsername().empty() && | 674 return service->signin()->GetAuthenticatedUsername().empty() && |
| 602 !HasUserSignedOut() && | 675 !HasUserSignedOut() && |
| 603 !google_services_username.empty() && | 676 !google_services_username.empty() && |
| 604 !lsid.empty() && | 677 !lsid.empty() && |
| 605 !sid.empty() && | 678 !sid.empty() && |
| 606 !encryption_bootstrap_token.empty() && | 679 !encryption_bootstrap_token.empty() && |
| 607 !service->setup_in_progress(); | 680 !service->setup_in_progress(); |
| 608 } | 681 } |
| 609 | 682 |
| 610 void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache() { | |
| 611 // We must reinitialize |alternate_store_| here because the underlying | |
| 612 // credential file in the alternate profile might have changed, and we must | |
| 613 // re-read it afresh. | |
| 614 if (alternate_store_.get()) { | |
| 615 alternate_store_->RemoveObserver(this); | |
| 616 alternate_store_.release(); | |
| 617 } | |
| 618 next_read_.Reset(base::Bind( | |
| 619 &CredentialCacheService::LookForCachedCredentialsInAlternateProfile, | |
| 620 weak_factory_.GetWeakPtr())); | |
| 621 MessageLoop::current()->PostDelayedTask( | |
| 622 FROM_HERE, | |
| 623 next_read_.callback(), | |
| 624 TimeDelta::FromSeconds(kCredentialCachePollIntervalSecs)); | |
| 625 } | |
| 626 | |
| 627 } // namespace syncer | 683 } // namespace syncer |
| OLD | NEW |