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" | |
21 #include "chrome/browser/signin/token_service.h" | 20 #include "chrome/browser/signin/token_service.h" |
22 #include "chrome/browser/signin/token_service_factory.h" | 21 #include "chrome/browser/signin/token_service_factory.h" |
23 #include "chrome/browser/sync/glue/chrome_encryptor.h" | 22 #include "chrome/browser/sync/glue/chrome_encryptor.h" |
24 #include "chrome/browser/sync/profile_sync_service.h" | 23 #include "chrome/browser/sync/profile_sync_service.h" |
25 #include "chrome/browser/sync/profile_sync_service_factory.h" | 24 #include "chrome/browser/sync/profile_sync_service_factory.h" |
26 #include "chrome/common/chrome_constants.h" | 25 #include "chrome/common/chrome_constants.h" |
27 #include "chrome/common/chrome_notification_types.h" | 26 #include "chrome/common/chrome_notification_types.h" |
28 #include "chrome/common/chrome_paths_internal.h" | 27 #include "chrome/common/chrome_paths_internal.h" |
29 #include "chrome/common/net/gaia/gaia_auth_consumer.h" | 28 #include "chrome/common/net/gaia/gaia_auth_consumer.h" |
30 #include "chrome/common/net/gaia/gaia_constants.h" | 29 #include "chrome/common/net/gaia/gaia_constants.h" |
(...skipping 18 matching lines...) Expand all Loading... |
49 using base::TimeDelta; | 48 using base::TimeDelta; |
50 using content::BrowserThread; | 49 using content::BrowserThread; |
51 | 50 |
52 CredentialCacheService::CredentialCacheService(Profile* profile) | 51 CredentialCacheService::CredentialCacheService(Profile* profile) |
53 : profile_(profile), | 52 : profile_(profile), |
54 // |profile_| is null in unit tests. | 53 // |profile_| is null in unit tests. |
55 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), | 54 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL), |
56 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | 55 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
57 if (profile_) { | 56 if (profile_) { |
58 InitializeLocalCredentialCacheWriter(); | 57 InitializeLocalCredentialCacheWriter(); |
59 // If sync is not disabled, look for credentials in the alternate profile. | 58 if (ShouldLookForCachedCredentialsInAlternateProfile()) |
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()) | |
64 LookForCachedCredentialsInAlternateProfile(); | 59 LookForCachedCredentialsInAlternateProfile(); |
65 } | 60 } |
66 } | 61 } |
67 | 62 |
68 CredentialCacheService::~CredentialCacheService() { | 63 CredentialCacheService::~CredentialCacheService() { |
69 Shutdown(); | 64 Shutdown(); |
70 } | 65 } |
71 | 66 |
72 void CredentialCacheService::Shutdown() { | 67 void CredentialCacheService::Shutdown() { |
73 local_store_observer_.release(); | 68 if (local_store_.get()) { |
74 local_store_.release(); | 69 local_store_.release(); |
75 alternate_store_observer_.release(); | 70 } |
76 alternate_store_.release(); | 71 |
| 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. |
77 } | 94 } |
78 | 95 |
79 void CredentialCacheService::Observe( | 96 void CredentialCacheService::Observe( |
80 int type, | 97 int type, |
81 const content::NotificationSource& source, | 98 const content::NotificationSource& source, |
82 const content::NotificationDetails& details) { | 99 const content::NotificationDetails& details) { |
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
84 DCHECK(local_store_.get()); | 101 DCHECK(local_store_.get()); |
85 switch (type) { | 102 switch (type) { |
86 case chrome::NOTIFICATION_PREF_CHANGED: { | 103 case chrome::NOTIFICATION_PREF_CHANGED: { |
87 const std::string pref_name = | 104 const std::string pref_name = |
88 *(content::Details<const std::string>(details).ptr()); | 105 *(content::Details<const std::string>(details).ptr()); |
89 if (pref_name == prefs::kSyncEncryptionBootstrapToken) { | 106 if (pref_name == prefs::kGoogleServicesUsername || |
90 PackAndUpdateStringPref(pref_name, | 107 pref_name == prefs::kSyncEncryptionBootstrapToken) { |
91 sync_prefs_.GetEncryptionBootstrapToken()); | 108 PackAndUpdateStringPref( |
| 109 pref_name, |
| 110 profile_->GetPrefs()->GetString(pref_name.c_str())); |
92 } else { | 111 } else { |
93 UpdateBooleanPref(pref_name, | 112 UpdateBooleanPref(pref_name, |
94 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); | 113 profile_->GetPrefs()->GetBoolean(pref_name.c_str())); |
95 } | 114 } |
96 break; | 115 break; |
97 } | 116 } |
98 | 117 |
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 | |
129 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { | 118 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: { |
130 const TokenService::CredentialsUpdatedDetails& token_details = | 119 const TokenService::CredentialsUpdatedDetails& token_details = |
131 *(content::Details<const TokenService::CredentialsUpdatedDetails>( | 120 *(content::Details<const TokenService::CredentialsUpdatedDetails>( |
132 details).ptr()); | 121 details).ptr()); |
133 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); | 122 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid()); |
134 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); | 123 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid()); |
135 break; | 124 break; |
136 } | 125 } |
137 | 126 |
138 case chrome::NOTIFICATION_TOKENS_CLEARED: { | 127 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
139 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); | 128 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string()); |
140 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); | 129 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string()); |
141 break; | 130 break; |
142 } | 131 } |
143 | 132 |
144 default: { | 133 default: { |
145 NOTREACHED(); | 134 NOTREACHED(); |
146 break; | 135 break; |
147 } | 136 } |
148 } | 137 } |
149 } | 138 } |
150 | 139 |
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 | |
297 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, | 140 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store, |
298 const std::string& pref_name) { | 141 const std::string& pref_name) { |
299 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); | 142 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK); |
300 } | 143 } |
301 | 144 |
302 // static | 145 // static |
303 base::StringValue* CredentialCacheService::PackCredential( | 146 base::StringValue* CredentialCacheService::PackCredential( |
304 const std::string& credential) { | 147 const std::string& credential) { |
305 // Do nothing for empty credentials. | 148 // Do nothing for empty credentials. |
306 if (credential.empty()) | 149 if (credential.empty()) |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 bool CredentialCacheService::GetBooleanPref( | 256 bool CredentialCacheService::GetBooleanPref( |
414 scoped_refptr<JsonPrefStore> store, | 257 scoped_refptr<JsonPrefStore> store, |
415 const std::string& pref_name) { | 258 const std::string& pref_name) { |
416 const base::Value* pref_value = NULL; | 259 const base::Value* pref_value = NULL; |
417 store->GetValue(pref_name, &pref_value); | 260 store->GetValue(pref_name, &pref_value); |
418 bool pref; | 261 bool pref; |
419 pref_value->GetAsBoolean(&pref); | 262 pref_value->GetAsBoolean(&pref); |
420 return pref; | 263 return pref; |
421 } | 264 } |
422 | 265 |
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 | |
482 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { | 266 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const { |
483 // The sync credential path in the default Desktop profile is | 267 // The sync credential path in the default Desktop profile is |
484 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while | 268 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while |
485 // the sync credential path in the default Metro profile is | 269 // the sync credential path in the default Metro profile is |
486 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". | 270 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials". |
487 DCHECK(profile_); | 271 DCHECK(profile_); |
488 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); | 272 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename); |
489 } | 273 } |
490 | 274 |
491 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { | 275 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const { |
492 DCHECK(profile_); | 276 DCHECK(profile_); |
493 FilePath alternate_user_data_dir; | 277 FilePath alternate_user_data_dir; |
494 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); | 278 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir); |
495 FilePath alternate_default_profile_dir = | 279 FilePath alternate_default_profile_dir = |
496 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); | 280 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir); |
497 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); | 281 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename); |
498 } | 282 } |
499 | 283 |
| 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 |
500 void CredentialCacheService::InitializeLocalCredentialCacheWriter() { | 296 void CredentialCacheService::InitializeLocalCredentialCacheWriter() { |
501 local_store_ = new JsonPrefStore( | 297 local_store_ = new JsonPrefStore( |
502 GetCredentialPathInCurrentProfile(), | 298 GetCredentialPathInCurrentProfile(), |
503 content::BrowserThread::GetMessageLoopProxyForThread( | 299 content::BrowserThread::GetMessageLoopProxyForThread( |
504 content::BrowserThread::FILE)); | 300 content::BrowserThread::FILE)); |
505 local_store_observer_ = new LocalStoreObserver(this, local_store_); | 301 local_store_->AddObserver(this); |
506 local_store_->ReadPrefsAsync(NULL); | 302 local_store_->ReadPrefsAsync(NULL); |
507 | 303 |
508 // Register for notifications for google sign in and sign out. | 304 // Register for notifications for updates to the sync credentials, which are |
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 | |
517 // stored in the PrefStore. | 305 // stored in the PrefStore. |
518 pref_registrar_.Init(profile_->GetPrefs()); | 306 pref_registrar_.Init(profile_->GetPrefs()); |
519 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); | 307 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this); |
| 308 pref_registrar_.Add(prefs::kGoogleServicesUsername, this); |
520 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); | 309 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this); |
521 ModelTypeSet all_types = syncer::ModelTypeSet::All(); | 310 ModelTypeSet all_types = syncer::ModelTypeSet::All(); |
522 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | 311 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { |
523 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. | 312 if (it.Get() == NIGORI) // The NIGORI preference is not persisted. |
524 continue; | 313 continue; |
525 pref_registrar_.Add( | 314 pref_registrar_.Add( |
526 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), | 315 browser_sync::SyncPrefs::GetPrefNameForDataType(it.Get()), |
527 this); | 316 this); |
528 } | 317 } |
529 | 318 |
530 // Register for notifications for updates to lsid and sid, which are stored in | 319 // Register for notifications for updates to lsid and sid, which are stored in |
531 // the TokenService. | 320 // the TokenService. |
532 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); | 321 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
533 registrar_.Add(this, | 322 registrar_.Add(this, |
534 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, | |
535 content::Source<TokenService>(token_service)); | |
536 registrar_.Add(this, | |
537 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, | 323 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED, |
538 content::Source<TokenService>(token_service)); | 324 content::Source<TokenService>(token_service)); |
539 registrar_.Add(this, | 325 registrar_.Add(this, |
540 chrome::NOTIFICATION_TOKENS_CLEARED, | 326 chrome::NOTIFICATION_TOKENS_CLEARED, |
541 content::Source<TokenService>(token_service)); | 327 content::Source<TokenService>(token_service)); |
542 } | 328 } |
543 | 329 |
544 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() { | 330 void CredentialCacheService::InitializeAlternateCredentialCacheReader( |
545 // Attempt to read cached credentials from the alternate profile. If no file | 331 bool* should_initialize) { |
546 // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned | 332 // If |should_initialize| is false, there was no credential cache in the |
547 // after initialization is complete. | 333 // alternate profile directory, and there is nothing to do right now. Schedule |
| 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. |
548 alternate_store_ = new JsonPrefStore( | 343 alternate_store_ = new JsonPrefStore( |
549 GetCredentialPathInAlternateProfile(), | 344 GetCredentialPathInAlternateProfile(), |
550 content::BrowserThread::GetMessageLoopProxyForThread( | 345 content::BrowserThread::GetMessageLoopProxyForThread( |
551 content::BrowserThread::FILE)); | 346 content::BrowserThread::FILE)); |
552 alternate_store_observer_ = new AlternateStoreObserver(this, | 347 alternate_store_->AddObserver(this); |
553 alternate_store_); | |
554 alternate_store_->ReadPrefsAsync(NULL); | 348 alternate_store_->ReadPrefsAsync(NULL); |
555 } | 349 } |
556 | 350 |
557 bool CredentialCacheService::HasUserSignedOut() { | 351 bool CredentialCacheService::HasUserSignedOut() { |
558 DCHECK(local_store_.get()); | 352 DCHECK(local_store_.get()); |
559 // If HasPref() is false, the user never signed in, since there are no | 353 // If HasPref() is false, the user never signed in, since there are no |
560 // previously cached credentials. If the kGoogleServicesUsername pref is | 354 // previously cached credentials. If the kGoogleServicesUsername pref is |
561 // empty, it means that the user signed in and subsequently signed out. | 355 // empty, it means that the user signed in and subsequently signed out. |
562 return HasPref(local_store_, prefs::kGoogleServicesUsername) && | 356 return HasPref(local_store_, prefs::kGoogleServicesUsername) && |
563 GetAndUnpackStringPref(local_store_, | 357 GetAndUnpackStringPref(local_store_, |
564 prefs::kGoogleServicesUsername).empty(); | 358 prefs::kGoogleServicesUsername).empty(); |
565 } | 359 } |
566 | 360 |
| 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 |
567 void CredentialCacheService::InitiateSignInWithCachedCredentials( | 491 void CredentialCacheService::InitiateSignInWithCachedCredentials( |
568 const std::string& google_services_username, | 492 const std::string& google_services_username, |
569 const std::string& encryption_bootstrap_token, | 493 const std::string& encryption_bootstrap_token, |
570 bool keep_everything_synced, | 494 bool keep_everything_synced, |
571 ModelTypeSet preferred_types) { | 495 ModelTypeSet preferred_types) { |
572 // Update the google username in the SigninManager and PrefStore. | 496 // Update the google username in the SigninManager and PrefStore. |
573 ProfileSyncService* service = | 497 ProfileSyncService* service = |
574 ProfileSyncServiceFactory::GetForProfile(profile_); | 498 ProfileSyncServiceFactory::GetForProfile(profile_); |
575 service->signin()->SetAuthenticatedUsername(google_services_username); | 499 service->signin()->SetAuthenticatedUsername(google_services_username); |
576 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, | 500 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
676 ProfileSyncServiceFactory::GetForProfile(profile_); | 600 ProfileSyncServiceFactory::GetForProfile(profile_); |
677 return service->signin()->GetAuthenticatedUsername().empty() && | 601 return service->signin()->GetAuthenticatedUsername().empty() && |
678 !HasUserSignedOut() && | 602 !HasUserSignedOut() && |
679 !google_services_username.empty() && | 603 !google_services_username.empty() && |
680 !lsid.empty() && | 604 !lsid.empty() && |
681 !sid.empty() && | 605 !sid.empty() && |
682 !encryption_bootstrap_token.empty() && | 606 !encryption_bootstrap_token.empty() && |
683 !service->setup_in_progress(); | 607 !service->setup_in_progress(); |
684 } | 608 } |
685 | 609 |
| 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 |
686 } // namespace syncer | 627 } // namespace syncer |
OLD | NEW |