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()) { |
Andrew T Wilson (Slow)
2012/08/11 01:40:20
BTW, I don't think you need to check if local_stor
Raghu Simha
2012/08/13 19:48:29
Done.
| |
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()); |
Andrew T Wilson (Slow)
2012/08/11 01:40:20
This is intended to catch both sign in and sign ou
Raghu Simha
2012/08/13 19:48:29
Yes, we need to listen to both sign in and sign ou
| |
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); | |
Andrew T Wilson (Slow)
2012/08/11 01:40:20
Where is the username written out in this case?
Raghu Simha
2012/08/13 19:48:29
The username (and other sync prefs) are written ou
| |
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() { | |
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(); | |
Andrew T Wilson (Slow)
2012/08/11 01:40:20
Note that you don't have to check for non-null bef
Raghu Simha
2012/08/13 19:48:29
Done.
| |
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 |