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 local_store_observer_.release(); |
69 local_store_.release(); | 74 local_store_.release(); |
70 } | 75 alternate_store_observer_.release(); |
71 | 76 alternate_store_.release(); |
72 if (alternate_store_.get()) { | |
73 alternate_store_->RemoveObserver(this); | |
74 alternate_store_.release(); | |
75 } | |
76 } | |
77 | |
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 } | 77 } |
95 | 78 |
96 void CredentialCacheService::Observe( | 79 void CredentialCacheService::Observe( |
97 int type, | 80 int type, |
98 const content::NotificationSource& source, | 81 const content::NotificationSource& source, |
99 const content::NotificationDetails& details) { | 82 const content::NotificationDetails& details) { |
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
101 DCHECK(local_store_.get()); | 84 DCHECK(local_store_.get()); |
102 switch (type) { | 85 switch (type) { |
103 case chrome::NOTIFICATION_PREF_CHANGED: { | 86 case chrome::NOTIFICATION_PREF_CHANGED: { |
104 const std::string pref_name = | 87 const std::string pref_name = |
105 *(content::Details<const std::string>(details).ptr()); | 88 *(content::Details<const std::string>(details).ptr()); |
106 if (pref_name == prefs::kGoogleServicesUsername || | 89 if (pref_name == prefs::kSyncEncryptionBootstrapToken) { |
107 pref_name == prefs::kSyncEncryptionBootstrapToken) { | 90 PackAndUpdateStringPref(pref_name, |
108 PackAndUpdateStringPref( | 91 sync_prefs_.GetEncryptionBootstrapToken()); |
109 pref_name, | |
110 profile_->GetPrefs()->GetString(pref_name.c_str())); | |
111 } else { | 92 } else { |
112 UpdateBooleanPref(pref_name, | 93 UpdateBooleanPref(pref_name, |
113 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); | 94 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); |
114 } | 95 } |
115 break; | 96 break; |
116 } | 97 } |
117 | 98 |
| 99 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: |
| 100 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: { |
| 101 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); |
| 102 PackAndUpdateStringPref(prefs::kGoogleServicesUsername, |
| 103 signin->GetAuthenticatedUsername()); |
| 104 break; |
| 105 } |
| 106 |
| 107 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { |
| 108 // If there is no existing local credential cache, and the token service |
| 109 // already has valid credentials as a result of the user having signed in, |
| 110 // write them to the cache. Used in cases where the user was already |
| 111 // signed in and then upgraded from a version of chrome that didn't |
| 112 // support credential caching. |
| 113 if (local_store_.get() && |
| 114 local_store_->IsInitializationComplete() && |
| 115 local_store_->GetReadError() == |
| 116 JsonPrefStore::PREF_READ_ERROR_NO_FILE) { |
| 117 TokenService* token_service = |
| 118 TokenServiceFactory::GetForProfile(profile_); |
| 119 if (token_service->AreCredentialsValid()) { |
| 120 GaiaAuthConsumer::ClientLoginResult credentials = |
| 121 token_service->credentials(); |
| 122 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, credentials.lsid); |
| 123 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, credentials.sid); |
| 124 } |
| 125 } |
| 126 break; |
| 127 } |
| 128 |
118 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { | 129 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { |
119 const TokenService::CredentialsUpdatedDetails& token_details = | 130 const TokenService::CredentialsUpdatedDetails& token_details = |
120 *(content::Details<const TokenService::CredentialsUpdatedDetails>( | 131 *(content::Details<const TokenService::CredentialsUpdatedDetails>( |
121 details).ptr()); | 132 details).ptr()); |
122 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); | 133 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); |
123 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); | 134 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); |
124 break; | 135 break; |
125 } | 136 } |
126 | 137 |
127 case chrome::NOTIFICATION_TOKENS_CLEARED: { | 138 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
128 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); | 139 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); |
129 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); | 140 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); |
130 break; | 141 break; |
131 } | 142 } |
132 | 143 |
133 default: { | 144 default: { |
134 NOTREACHED(); | 145 NOTREACHED(); |
135 break; | 146 break; |
136 } | 147 } |
137 } | 148 } |
138 } | 149 } |
139 | 150 |
| 151 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() { |
| 152 // If the local user has signed in and signed out, we do not consume cached |
| 153 // credentials from the alternate profile. There is nothing more to do, now or |
| 154 // later on. |
| 155 if (HasUserSignedOut()) |
| 156 return; |
| 157 |
| 158 // Sanity check the alternate credential cache. If any string credentials |
| 159 // are outright missing even though the file exists, something is awry with |
| 160 // the alternate profile store. There is no sense in flagging an error as the |
| 161 // problem lies in a different profile directory. There is nothing to do now. |
| 162 // We schedule a future read from the alternate credential cache and return. |
| 163 DCHECK(alternate_store_.get()); |
| 164 if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) || |
| 165 !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) || |
| 166 !HasPref(alternate_store_, GaiaConstants::kGaiaSid) || |
| 167 !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) || |
| 168 !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) { |
| 169 VLOG(1) << "Could not find cached credentials in \"" |
| 170 << GetCredentialPathInAlternateProfile().value() << "\"."; |
| 171 ScheduleNextReadFromAlternateCredentialCache(); |
| 172 return; |
| 173 } |
| 174 |
| 175 // Extract cached credentials from the alternate credential cache. |
| 176 std::string google_services_username = |
| 177 GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername); |
| 178 std::string lsid = |
| 179 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid); |
| 180 std::string sid = |
| 181 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid); |
| 182 std::string encryption_bootstrap_token = |
| 183 GetAndUnpackStringPref(alternate_store_, |
| 184 prefs::kSyncEncryptionBootstrapToken); |
| 185 |
| 186 // Sign out of sync if the alternate profile has signed out the same user. |
| 187 // There is no need to schedule any more reads of the alternate profile |
| 188 // cache because we only apply cached credentials for first-time sign-ins. |
| 189 if (ShouldSignOutOfSync(google_services_username)) { |
| 190 VLOG(1) << "User has signed out on the other profile. Signing out."; |
| 191 InitiateSignOut(); |
| 192 return; |
| 193 } |
| 194 |
| 195 // Extract cached sync prefs from the alternate credential cache. |
| 196 bool keep_everything_synced = |
| 197 GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced); |
| 198 ProfileSyncService* service = |
| 199 ProfileSyncServiceFactory::GetForProfile(profile_); |
| 200 ModelTypeSet registered_types = service->GetRegisteredDataTypes(); |
| 201 ModelTypeSet preferred_types; |
| 202 for (ModelTypeSet::Iterator it = registered_types.First(); |
| 203 it.Good(); |
| 204 it.Inc()) { |
| 205 std::string datatype_pref_name = |
| 206 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); |
| 207 if (!HasPref(alternate_store_, datatype_pref_name)) { |
| 208 // If there is no cached pref for a specific data type, it means that the |
| 209 // user originally signed in with an older version of Chrome, and then |
| 210 // upgraded to a version with a new datatype. In such cases, we leave the |
| 211 // default initial datatype setting as false while reading cached |
| 212 // credentials, just like we do in SyncPrefs::RegisterPreferences. |
| 213 VLOG(1) << "Could not find cached datatype pref for " |
| 214 << datatype_pref_name << " in " |
| 215 << GetCredentialPathInAlternateProfile().value() << "."; |
| 216 continue; |
| 217 } |
| 218 if (GetBooleanPref(alternate_store_, datatype_pref_name)) |
| 219 preferred_types.Put(it.Get()); |
| 220 } |
| 221 |
| 222 // Reconfigure if sync settings or credentials have changed in the alternate |
| 223 // profile, but for the same user that is signed in to the local profile. |
| 224 if (MayReconfigureSync(google_services_username)) { |
| 225 if (HaveSyncPrefsChanged(keep_everything_synced, preferred_types)) { |
| 226 VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring."; |
| 227 service->OnUserChoseDatatypes(keep_everything_synced, preferred_types); |
| 228 } |
| 229 if (HaveTokenServiceCredentialsChanged(lsid, sid)) { |
| 230 VLOG(1) << "Token service credentials have changed in other profile."; |
| 231 UpdateTokenServiceCredentials(lsid, sid); |
| 232 } |
| 233 } |
| 234 |
| 235 // Sign in if we notice new cached credentials in the alternate profile. |
| 236 if (ShouldSignInToSync(google_services_username, |
| 237 lsid, |
| 238 sid, |
| 239 encryption_bootstrap_token)) { |
| 240 InitiateSignInWithCachedCredentials(google_services_username, |
| 241 encryption_bootstrap_token, |
| 242 keep_everything_synced, |
| 243 preferred_types); |
| 244 UpdateTokenServiceCredentials(lsid, sid); |
| 245 } |
| 246 |
| 247 // Schedule the next read from the alternate credential cache so that we can |
| 248 // detect future reconfigures or sign outs. |
| 249 ScheduleNextReadFromAlternateCredentialCache(); |
| 250 } |
| 251 |
| 252 void CredentialCacheService::WriteExistingSyncPrefsToLocalCache() { |
| 253 // If the local user is already signed in and there is no local credential |
| 254 // cache file, write all the existing sync prefs to the local cache. |
| 255 DCHECK(local_store_.get() && |
| 256 local_store_->GetReadError() == |
| 257 JsonPrefStore::PREF_READ_ERROR_NO_FILE); |
| 258 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_); |
| 259 if (!signin->GetAuthenticatedUsername().empty() && |
| 260 !HasPref(local_store_, prefs::kGoogleServicesUsername)) { |
| 261 PackAndUpdateStringPref(prefs::kGoogleServicesUsername, |
| 262 signin->GetAuthenticatedUsername()); |
| 263 PackAndUpdateStringPref(prefs::kSyncEncryptionBootstrapToken, |
| 264 sync_prefs_.GetEncryptionBootstrapToken()); |
| 265 UpdateBooleanPref(prefs::kSyncKeepEverythingSynced, |
| 266 sync_prefs_.HasKeepEverythingSynced()); |
| 267 ProfileSyncService* service = |
| 268 ProfileSyncServiceFactory::GetForProfile(profile_); |
| 269 ModelTypeSet registered_types = service->GetRegisteredDataTypes(); |
| 270 for (ModelTypeSet::Iterator it = registered_types.First(); |
| 271 it.Good(); |
| 272 it.Inc()) { |
| 273 std::string datatype_pref_name = |
| 274 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()); |
| 275 UpdateBooleanPref( |
| 276 datatype_pref_name, |
| 277 profile_->GetPrefs()->GetBoolean(datatype_pref_name.c_str())); |
| 278 } |
| 279 } |
| 280 } |
| 281 |
| 282 void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache() { |
| 283 // We must reinitialize |alternate_store_| here because the underlying |
| 284 // credential file in the alternate profile might have changed, and we must |
| 285 // re-read it afresh. |
| 286 alternate_store_observer_.release(); |
| 287 alternate_store_.release(); |
| 288 next_read_.Reset(base::Bind( |
| 289 &CredentialCacheService::LookForCachedCredentialsInAlternateProfile, |
| 290 weak_factory_.GetWeakPtr())); |
| 291 MessageLoop::current()->PostDelayedTask( |
| 292 FROM_HERE, |
| 293 next_read_.callback(), |
| 294 TimeDelta::FromSeconds(kCredentialCachePollIntervalSecs)); |
| 295 } |
| 296 |
140 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, | 297 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, |
141 const std::string& pref_name) { | 298 const std::string& pref_name) { |
142 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); | 299 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); |
143 } | 300 } |
144 | 301 |
145 // static | 302 // static |
146 base::StringValue* CredentialCacheService::PackCredential( | 303 base::StringValue* CredentialCacheService::PackCredential( |
147 const std::string& credential) { | 304 const std::string& credential) { |
148 // Do nothing for empty credentials. | 305 // Do nothing for empty credentials. |
149 if (credential.empty()) | 306 if (credential.empty()) |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 bool CredentialCacheService::GetBooleanPref( | 413 bool CredentialCacheService::GetBooleanPref( |
257 scoped_refptr<JsonPrefStore> store, | 414 scoped_refptr<JsonPrefStore> store, |
258 const std::string& pref_name) { | 415 const std::string& pref_name) { |
259 const base::Value* pref_value = NULL; | 416 const base::Value* pref_value = NULL; |
260 store->GetValue(pref_name, &pref_value); | 417 store->GetValue(pref_name, &pref_value); |
261 bool pref; | 418 bool pref; |
262 pref_value->GetAsBoolean(&pref); | 419 pref_value->GetAsBoolean(&pref); |
263 return pref; | 420 return pref; |
264 } | 421 } |
265 | 422 |
| 423 CredentialCacheService::LocalStoreObserver::LocalStoreObserver( |
| 424 CredentialCacheService* service, |
| 425 scoped_refptr<JsonPrefStore> local_store) |
| 426 : service_(service), |
| 427 local_store_(local_store) { |
| 428 local_store_->AddObserver(this); |
| 429 } |
| 430 |
| 431 CredentialCacheService::LocalStoreObserver::~LocalStoreObserver() { |
| 432 local_store_->RemoveObserver(this); |
| 433 } |
| 434 |
| 435 void CredentialCacheService::LocalStoreObserver::OnInitializationCompleted( |
| 436 bool succeeded) { |
| 437 // If there is no existing local credential cache, write any existing sync |
| 438 // prefs to the local cache. This could happen if the user was already signed |
| 439 // in and restarts chrome after upgrading from an older version that didn't |
| 440 // support credential caching. Note that |succeeded| will be true even if |
| 441 // the local cache file wasn't found, so long as its parent dir was found. |
| 442 DCHECK(succeeded); |
| 443 if (local_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NO_FILE) { |
| 444 service_->WriteExistingSyncPrefsToLocalCache(); |
| 445 } |
| 446 } |
| 447 |
| 448 void CredentialCacheService::LocalStoreObserver::OnPrefValueChanged( |
| 449 const std::string& key) { |
| 450 // Nothing to do here, since credentials are cached silently. |
| 451 } |
| 452 |
| 453 CredentialCacheService::AlternateStoreObserver::AlternateStoreObserver( |
| 454 CredentialCacheService* service, |
| 455 scoped_refptr<JsonPrefStore> alternate_store) |
| 456 : service_(service), |
| 457 alternate_store_(alternate_store) { |
| 458 alternate_store_->AddObserver(this); |
| 459 } |
| 460 |
| 461 CredentialCacheService::AlternateStoreObserver::~AlternateStoreObserver() { |
| 462 alternate_store_->RemoveObserver(this); |
| 463 } |
| 464 |
| 465 void CredentialCacheService::AlternateStoreObserver::OnInitializationCompleted( |
| 466 bool succeeded) { |
| 467 // If an alternate credential cache was found, begin consuming its contents. |
| 468 // If not, schedule a future read. |
| 469 if (succeeded && |
| 470 alternate_store_->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE) { |
| 471 service_->ReadCachedCredentialsFromAlternateProfile(); |
| 472 } else { |
| 473 service_->ScheduleNextReadFromAlternateCredentialCache(); |
| 474 } |
| 475 } |
| 476 |
| 477 void CredentialCacheService::AlternateStoreObserver::OnPrefValueChanged( |
| 478 const std::string& key) { |
| 479 // Nothing to do here, since credentials are cached silently. |
| 480 } |
| 481 |
266 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { | 482 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { |
267 // The sync credential path in the default Desktop profile is | 483 // The sync credential path in the default Desktop profile is |
268 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while | 484 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while |
269 // the sync credential path in the default Metro profile is | 485 // the sync credential path in the default Metro profile is |
270 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". | 486 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". |
271 DCHECK(profile_); | 487 DCHECK(profile_); |
272 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); | 488 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); |
273 } | 489 } |
274 | 490 |
275 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { | 491 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { |
276 DCHECK(profile_); | 492 DCHECK(profile_); |
277 FilePath alternate_user_data_dir; | 493 FilePath alternate_user_data_dir; |
278 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); | 494 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); |
279 FilePath alternate_default_profile_dir = | 495 FilePath alternate_default_profile_dir = |
280 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); | 496 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); |
281 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); | 497 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); |
282 } | 498 } |
283 | 499 |
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() { | 500 void CredentialCacheService::InitializeLocalCredentialCacheWriter() { |
297 local_store_ = new JsonPrefStore( | 501 local_store_ = new JsonPrefStore( |
298 GetCredentialPathInCurrentProfile(), | 502 GetCredentialPathInCurrentProfile(), |
299 content::BrowserThread::GetMessageLoopProxyForThread( | 503 content::BrowserThread::GetMessageLoopProxyForThread( |
300 content::BrowserThread::FILE)); | 504 content::BrowserThread::FILE)); |
301 local_store_->AddObserver(this); | 505 local_store_observer_ = new LocalStoreObserver(this, local_store_); |
302 local_store_->ReadPrefsAsync(NULL); | 506 local_store_->ReadPrefsAsync(NULL); |
303 | 507 |
304 // Register for notifications for updates to the sync credentials, which are | 508 // Register for notifications for google sign in and sign out. |
| 509 registrar_.Add(this, |
| 510 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, |
| 511 content::Source<Profile>(profile_)); |
| 512 registrar_.Add(this, |
| 513 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, |
| 514 content::Source<Profile>(profile_)); |
| 515 |
| 516 // Register for notifications for updates to various sync settings, which are |
305 // stored in the PrefStore. | 517 // stored in the PrefStore. |
306 pref_registrar_.Init(profile_->GetPrefs()); | 518 pref_registrar_.Init(profile_->GetPrefs()); |
307 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); | 519 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); |
308 pref_registrar_.Add(prefs::kGoogleServicesUsername, this); | |
309 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); | 520 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); |
310 ModelTypeSet all_types = syncer::ModelTypeSet::All(); | 521 ModelTypeSet all_types = syncer::ModelTypeSet::All(); |
311 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | 522 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { |
312 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. | 523 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. |
313 continue; | 524 continue; |
314 pref_registrar_.Add( | 525 pref_registrar_.Add( |
315 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), | 526 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), |
316 this); | 527 this); |
317 } | 528 } |
318 | 529 |
319 // Register for notifications for updates to lsid and sid, which are stored in | 530 // Register for notifications for updates to lsid and sid, which are stored in |
320 // the TokenService. | 531 // the TokenService. |
321 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); | 532 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
322 registrar_.Add(this, | 533 registrar_.Add(this, |
| 534 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, |
| 535 content::Source<TokenService>(token_service)); |
| 536 registrar_.Add(this, |
323 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, | 537 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, |
324 content::Source<TokenService>(token_service)); | 538 content::Source<TokenService>(token_service)); |
325 registrar_.Add(this, | 539 registrar_.Add(this, |
326 chrome::NOTIFICATION_TOKENS_CLEARED, | 540 chrome::NOTIFICATION_TOKENS_CLEARED, |
327 content::Source<TokenService>(token_service)); | 541 content::Source<TokenService>(token_service)); |
328 } | 542 } |
329 | 543 |
330 void CredentialCacheService::InitializeAlternateCredentialCacheReader( | 544 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { |
331 bool* should_initialize) { | 545 // 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 | 546 // 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 | 547 // 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( | 548 alternate_store_ = new JsonPrefStore( |
344 GetCredentialPathInAlternateProfile(), | 549 GetCredentialPathInAlternateProfile(), |
345 content::BrowserThread::GetMessageLoopProxyForThread( | 550 content::BrowserThread::GetMessageLoopProxyForThread( |
346 content::BrowserThread::FILE)); | 551 content::BrowserThread::FILE)); |
347 alternate_store_->AddObserver(this); | 552 alternate_store_observer_ = new AlternateStoreObserver(this, |
| 553 alternate_store_); |
348 alternate_store_->ReadPrefsAsync(NULL); | 554 alternate_store_->ReadPrefsAsync(NULL); |
349 } | 555 } |
350 | 556 |
351 bool CredentialCacheService::HasUserSignedOut() { | 557 bool CredentialCacheService::HasUserSignedOut() { |
352 DCHECK(local_store_.get()); | 558 DCHECK(local_store_.get()); |
353 // If HasPref() is false, the user never signed in, since there are no | 559 // If HasPref() is false, the user never signed in, since there are no |
354 // previously cached credentials. If the kGoogleServicesUsername pref is | 560 // previously cached credentials. If the kGoogleServicesUsername pref is |
355 // empty, it means that the user signed in and subsequently signed out. | 561 // empty, it means that the user signed in and subsequently signed out. |
356 return HasPref(local_store_, prefs::kGoogleServicesUsername) && | 562 return HasPref(local_store_, prefs::kGoogleServicesUsername) && |
357 GetAndUnpackStringPref(local_store_, | 563 GetAndUnpackStringPref(local_store_, |
358 prefs::kGoogleServicesUsername).empty(); | 564 prefs::kGoogleServicesUsername).empty(); |
359 } | 565 } |
360 | 566 |
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( | 567 void CredentialCacheService::InitiateSignInWithCachedCredentials( |
492 const std::string& google_services_username, | 568 const std::string& google_services_username, |
493 const std::string& encryption_bootstrap_token, | 569 const std::string& encryption_bootstrap_token, |
494 bool keep_everything_synced, | 570 bool keep_everything_synced, |
495 ModelTypeSet preferred_types) { | 571 ModelTypeSet preferred_types) { |
496 // Update the google username in the SigninManager and PrefStore. | 572 // Update the google username in the SigninManager and PrefStore. |
497 ProfileSyncService* service = | 573 ProfileSyncService* service = |
498 ProfileSyncServiceFactory::GetForProfile(profile_); | 574 ProfileSyncServiceFactory::GetForProfile(profile_); |
499 service->signin()->SetAuthenticatedUsername(google_services_username); | 575 service->signin()->SetAuthenticatedUsername(google_services_username); |
500 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, | 576 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 ProfileSyncServiceFactory::GetForProfile(profile_); | 676 ProfileSyncServiceFactory::GetForProfile(profile_); |
601 return service->signin()->GetAuthenticatedUsername().empty() && | 677 return service->signin()->GetAuthenticatedUsername().empty() && |
602 !HasUserSignedOut() && | 678 !HasUserSignedOut() && |
603 !google_services_username.empty() && | 679 !google_services_username.empty() && |
604 !lsid.empty() && | 680 !lsid.empty() && |
605 !sid.empty() && | 681 !sid.empty() && |
606 !encryption_bootstrap_token.empty() && | 682 !encryption_bootstrap_token.empty() && |
607 !service->setup_in_progress(); | 683 !service->setup_in_progress(); |
608 } | 684 } |
609 | 685 |
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 | 686 } // namespace syncer |
OLD | NEW |