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
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/credential_cache_service_win.h"
7 #include "base/base64.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/file_util.h"
13 #include "base/string_number_conversions.h"
14 #include "base/time.h"
15 #include "base/values.h"
16 #include "base/win/windows_version.h"
17 #include "chrome/browser/prefs/pref_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/signin/signin_manager.h"
21 #include "chrome/browser/signin/signin_manager_factory.h"
22 #include "chrome/browser/signin/token_service.h"
23 #include "chrome/browser/signin/token_service_factory.h"
24 #include "chrome/browser/sync/credential_cache_service_factory_win.h"
25 #include "chrome/browser/sync/glue/chrome_encryptor.h"
26 #include "chrome/browser/sync/profile_sync_service.h"
27 #include "chrome/browser/sync/profile_sync_service_factory.h"
28 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_notification_types.h"
30 #include "chrome/common/chrome_paths_internal.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_details.h"
35 #include "content/public/browser/notification_source.h"
36 #include "google_apis/gaia/gaia_auth_consumer.h"
37 #include "google_apis/gaia/gaia_constants.h"
38 #include "sync/internal_api/public/base/model_type.h"
42 // The time delay (in seconds) between two consecutive polls of the alternate
43 // credential cache. A two minute delay seems like a reasonable amount of time
44 // in which to propagate changes to signed in state between Metro and Desktop.
45 const int kCredentialCachePollIntervalSecs
= 2 * 60;
47 // Keeps track of the last time a credential cache was written to. Used to make
48 // sure that we only apply changes from newer credential caches to older ones,
49 // and not vice versa.
50 const char kLastCacheUpdateTimeKey
[] = "last_cache_update_time";
52 // Deprecated. We were previously using base::TimeTicks as a timestamp. This was
53 // bad because TimeTicks values roll over on machine restart. We now use
54 // base::Time as a timestamp. However, we can't simply reuse "last_updated_time"
55 // because it may result in a Time value being compared with a TimeTicks value.
56 const char kLastUpdatedTimeTicksDeprecated
[] = "last_updated_time";
58 using base::TimeDelta
;
59 using content::BrowserThread
;
61 CredentialCacheService::CredentialCacheService(Profile
* profile
)
63 // |profile_| is null in unit tests.
64 sync_prefs_(profile_
? profile_
->GetPrefs() : NULL
),
65 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
67 InitializeLocalCredentialCacheWriter();
68 // If sync is not disabled, look for credentials in the alternate profile.
69 // Note that we do want to look for credentials in the alternate profile
70 // even if the local user is signed in, so that we can detect a sign out or
71 // reconfigure originating from the alternate profile.
72 if (!sync_prefs_
.IsManaged())
73 LookForCachedCredentialsInAlternateProfile();
77 CredentialCacheService::~CredentialCacheService() {
81 void CredentialCacheService::Shutdown() {
82 if (local_store_
.get())
83 local_store_
->CommitPendingWrite();
84 local_store_observer_
.release();
85 local_store_
.release();
86 alternate_store_observer_
.release();
87 alternate_store_
.release();
90 void CredentialCacheService::Observe(
92 const content::NotificationSource
& source
,
93 const content::NotificationDetails
& details
) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
95 DCHECK(local_store_
.get());
97 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT
: {
98 // The user has signed out. Write blank values to the google username,
99 // encryption tokens and token service credentials in the local cache.
100 PackAndUpdateStringPref(prefs::kGoogleServicesUsername
, std::string());
101 if (HasPref(local_store_
, prefs::kSyncEncryptionBootstrapToken
)) {
102 PackAndUpdateStringPref(prefs::kSyncEncryptionBootstrapToken
,
105 if (HasPref(local_store_
, prefs::kSyncKeystoreEncryptionBootstrapToken
)) {
106 PackAndUpdateStringPref(prefs::kSyncKeystoreEncryptionBootstrapToken
,
109 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid
, std::string());
110 PackAndUpdateStringPref(GaiaConstants::kGaiaSid
, std::string());
114 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
: {
115 // The user has signed in. Write the new value of the google username to
117 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
118 PackAndUpdateStringPref(prefs::kGoogleServicesUsername
,
119 signin
->GetAuthenticatedUsername());
123 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
: {
124 // Local sync configuration is done. The sync service is now ready to
125 // be reconfigured. Immediately look for any unconsumed config changes in
126 // the alternate profile. If all changes have been consumed, this is a
128 ScheduleNextReadFromAlternateCredentialCache(0);
132 case chrome::NOTIFICATION_SYNC_CONFIGURE_START
: {
133 // We have detected a sync sign in, auto-start or reconfigure. Write the
134 // latest sync preferences to the local cache.
135 WriteSyncPrefsToLocalCache();
139 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED
: {
140 // The token service has been fully initialized. Update the token service
141 // credentials in the local cache. This is a no-op if the cache already
142 // contains the latest values.
143 TokenService
* token_service
=
144 TokenServiceFactory::GetForProfile(profile_
);
145 if (token_service
->AreCredentialsValid()) {
146 GaiaAuthConsumer::ClientLoginResult credentials
=
147 token_service
->credentials();
148 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid
, credentials
.lsid
);
149 PackAndUpdateStringPref(GaiaConstants::kGaiaSid
, credentials
.sid
);
154 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED
: {
155 // The token service has new credentials. Write them to the local cache.
156 const TokenService::CredentialsUpdatedDetails
& token_details
=
157 *(content::Details
<const TokenService::CredentialsUpdatedDetails
>(
159 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid
, token_details
.lsid());
160 PackAndUpdateStringPref(GaiaConstants::kGaiaSid
, token_details
.sid());
164 case chrome::NOTIFICATION_TOKENS_CLEARED
: {
165 // Tokens have been cleared. Blank out lsid and sid in the local cache.
166 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid
, std::string());
167 PackAndUpdateStringPref(GaiaConstants::kGaiaSid
, std::string());
178 void CredentialCacheService::OnPreferenceChanged(PrefServiceBase
* service
,
179 const std::string
& pref_name
) {
180 // One of the two sync encryption tokens has changed. Update its value in
182 if (pref_name
== prefs::kSyncEncryptionBootstrapToken
) {
183 PackAndUpdateStringPref(pref_name
,
184 sync_prefs_
.GetEncryptionBootstrapToken());
185 } else if (pref_name
== prefs::kSyncKeystoreEncryptionBootstrapToken
) {
186 PackAndUpdateStringPref(
188 sync_prefs_
.GetKeystoreEncryptionBootstrapToken());
190 NOTREACHED() "Invalid pref name " << pref_name
<< ".";
194 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() {
195 // If the local user has signed in and signed out, we do not consume cached
196 // credentials from the alternate profile. There is nothing more to do, now or
198 if (HasUserSignedOut())
201 // Sanity check the alternate credential cache. Note that it is sufficient to
202 // have just one of the two sync encryption tokens. If any string credentials
203 // are outright missing even though the file exists, something is awry with
204 // the alternate profile store. There is no sense in flagging an error as the
205 // problem lies in a different profile directory. There is nothing to do now.
206 // We schedule a future read from the alternate credential cache and return.
207 DCHECK(alternate_store_
.get());
208 if (!HasPref(alternate_store_
, prefs::kGoogleServicesUsername
) ||
209 !HasPref(alternate_store_
, GaiaConstants::kGaiaLsid
) ||
210 !HasPref(alternate_store_
, GaiaConstants::kGaiaSid
) ||
211 !(HasPref(alternate_store_
, prefs::kSyncEncryptionBootstrapToken
) ||
212 HasPref(alternate_store_
,
213 prefs::kSyncKeystoreEncryptionBootstrapToken
)) ||
214 !HasPref(alternate_store_
, prefs::kSyncKeepEverythingSynced
)) {
215 VLOG(1) << "Could not find cached credentials in \""
216 << GetCredentialPathInAlternateProfile().value() << "\".";
217 ScheduleNextReadFromAlternateCredentialCache(
218 kCredentialCachePollIntervalSecs
);
222 // Extract the google username, lsid and sid from the alternate credential
224 std::string alternate_google_services_username
=
225 GetAndUnpackStringPref(alternate_store_
, prefs::kGoogleServicesUsername
);
226 std::string alternate_lsid
=
227 GetAndUnpackStringPref(alternate_store_
, GaiaConstants::kGaiaLsid
);
228 std::string alternate_sid
=
229 GetAndUnpackStringPref(alternate_store_
, GaiaConstants::kGaiaSid
);
231 // Extract the sync encryption tokens from the alternate credential cache.
232 // Both tokens may not be found, since only one of them is used at any time.
233 std::string alternate_encryption_bootstrap_token
;
234 if (HasPref(alternate_store_
, prefs::kSyncEncryptionBootstrapToken
)) {
235 alternate_encryption_bootstrap_token
=
236 GetAndUnpackStringPref(alternate_store_
,
237 prefs::kSyncEncryptionBootstrapToken
);
239 std::string alternate_keystore_encryption_bootstrap_token
;
240 if (HasPref(alternate_store_
, prefs::kSyncKeystoreEncryptionBootstrapToken
)) {
241 alternate_keystore_encryption_bootstrap_token
=
242 GetAndUnpackStringPref(alternate_store_
,
243 prefs::kSyncKeystoreEncryptionBootstrapToken
);
246 // Sign out of sync if the alternate profile has signed out the same user.
247 // There is no need to schedule any more reads of the alternate profile
248 // cache because we only apply cached credentials for first-time sign-ins.
249 if (ShouldSignOutOfSync(alternate_google_services_username
)) {
250 VLOG(1) << "User has signed out on the other profile. Signing out.";
255 // Extract cached sync prefs from the alternate credential cache.
256 bool alternate_keep_everything_synced
=
257 GetBooleanPref(alternate_store_
, prefs::kSyncKeepEverythingSynced
);
258 ProfileSyncService
* service
=
259 ProfileSyncServiceFactory::GetForProfile(profile_
);
260 ModelTypeSet registered_types
= service
->GetRegisteredDataTypes();
261 ModelTypeSet alternate_preferred_types
;
262 for (ModelTypeSet::Iterator it
= registered_types
.First();
265 std::string datatype_pref_name
=
266 browser_sync::SyncPrefs::GetPrefNameForDataType(it
.Get());
267 if (!HasPref(alternate_store_
, datatype_pref_name
)) {
268 // If there is no cached pref for a specific data type, it means that the
269 // user originally signed in with an older version of Chrome, and then
270 // upgraded to a version with a new datatype. In such cases, we leave the
271 // default initial datatype setting as false while reading cached
272 // credentials, just like we do in SyncPrefs::RegisterPreferences.
273 VLOG(1) << "Could not find cached datatype pref for "
274 << datatype_pref_name
<< " in "
275 << GetCredentialPathInAlternateProfile().value() << ".";
278 if (GetBooleanPref(alternate_store_
, datatype_pref_name
))
279 alternate_preferred_types
.Put(it
.Get());
282 // Reconfigure if sync settings, encryption tokens or token service
283 // credentials have changed in the alternate profile, but for the same user
284 // that is signed in to the local profile.
285 if (MayReconfigureSync(alternate_google_services_username
)) {
286 if (HaveSyncPrefsChanged(alternate_keep_everything_synced
,
287 alternate_preferred_types
)) {
288 VLOG(1) << "Sync prefs have changed in other profile. Reconfiguring.";
289 service
->OnUserChoseDatatypes(alternate_keep_everything_synced
,
290 alternate_preferred_types
);
292 if (HaveSyncEncryptionTokensChanged(
293 alternate_encryption_bootstrap_token
,
294 alternate_keystore_encryption_bootstrap_token
)) {
295 VLOG(1) << "Sync encryption tokens have changed in other profile.";
296 sync_prefs_
.SetEncryptionBootstrapToken(
297 alternate_encryption_bootstrap_token
);
298 sync_prefs_
.SetKeystoreEncryptionBootstrapToken(
299 alternate_keystore_encryption_bootstrap_token
);
301 if (HaveTokenServiceCredentialsChanged(alternate_lsid
, alternate_sid
)) {
302 VLOG(1) << "Token service credentials have changed in other profile.";
303 UpdateTokenServiceCredentials(alternate_lsid
, alternate_sid
);
307 // Sign in if we notice new cached credentials in the alternate profile.
308 if (ShouldSignInToSync(alternate_google_services_username
,
311 alternate_encryption_bootstrap_token
,
312 alternate_keystore_encryption_bootstrap_token
)) {
313 InitiateSignInWithCachedCredentials(
314 alternate_google_services_username
,
315 alternate_encryption_bootstrap_token
,
316 alternate_keystore_encryption_bootstrap_token
,
317 alternate_keep_everything_synced
,
318 alternate_preferred_types
);
319 UpdateTokenServiceCredentials(alternate_lsid
, alternate_sid
);
322 // Schedule the next read from the alternate credential cache so that we can
323 // detect future reconfigures or sign outs.
324 ScheduleNextReadFromAlternateCredentialCache(
325 kCredentialCachePollIntervalSecs
);
328 void CredentialCacheService::WriteSyncPrefsToLocalCache() {
329 UpdateBooleanPref(prefs::kSyncKeepEverythingSynced
,
330 sync_prefs_
.HasKeepEverythingSynced());
331 ProfileSyncService
* service
=
332 ProfileSyncServiceFactory::GetForProfile(profile_
);
333 ModelTypeSet registered_types
= service
->GetRegisteredDataTypes();
334 for (ModelTypeSet::Iterator it
= registered_types
.First();
337 std::string datatype_pref_name
=
338 browser_sync::SyncPrefs::GetPrefNameForDataType(it
.Get());
341 profile_
->GetPrefs()->GetBoolean(datatype_pref_name
.c_str()));
345 void CredentialCacheService::ScheduleNextReadFromAlternateCredentialCache(
347 DCHECK_LE(0, delay_secs
);
348 // We must reinitialize |alternate_store_| here because the underlying
349 // credential file in the alternate profile might have changed, and we must
350 // re-read it afresh.
351 alternate_store_observer_
.release();
352 alternate_store_
.release();
353 next_read_
.Reset(base::Bind(
354 &CredentialCacheService::LookForCachedCredentialsInAlternateProfile
,
355 weak_factory_
.GetWeakPtr()));
356 MessageLoop::current()->PostDelayedTask(
358 next_read_
.callback(),
359 TimeDelta::FromSeconds(delay_secs
));
362 bool CredentialCacheService::HasPref(scoped_refptr
<JsonPrefStore
> store
,
363 const std::string
& pref_name
) {
364 return store
->GetValue(pref_name
, NULL
);
368 base::StringValue
* CredentialCacheService::PackCredential(
369 const std::string
& credential
) {
370 // Do nothing for empty credentials.
371 if (credential
.empty())
372 return base::Value::CreateStringValue("");
374 browser_sync::ChromeEncryptor encryptor
;
375 std::string encrypted
;
376 if (!encryptor
.EncryptString(credential
, &encrypted
)) {
378 return base::Value::CreateStringValue(std::string());
382 if (!base::Base64Encode(encrypted
, &encoded
)) {
384 return base::Value::CreateStringValue(std::string());
387 return base::Value::CreateStringValue(encoded
);
391 std::string
CredentialCacheService::UnpackCredential(
392 const base::Value
& packed
) {
394 if (!packed
.GetAsString(&encoded
)) {
396 return std::string();
399 // Do nothing for empty credentials.
401 return std::string();
403 std::string encrypted
;
404 if (!base::Base64Decode(encoded
, &encrypted
)) {
406 return std::string();
409 browser_sync::ChromeEncryptor encryptor
;
410 std::string unencrypted
;
411 if (!encryptor
.DecryptString(encrypted
, &unencrypted
)) {
413 return std::string();
419 void CredentialCacheService::WriteLastCacheUpdateTime() {
420 DCHECK(local_store_
.get());
421 int64 last_cache_update_time_int64
= base::Time::Now().ToInternalValue();
422 std::string last_cache_update_time_string
=
423 base::Int64ToString(last_cache_update_time_int64
);
424 local_store_
->SetValueSilently(
425 kLastCacheUpdateTimeKey
,
426 base::Value::CreateStringValue(last_cache_update_time_string
));
429 void CredentialCacheService::PackAndUpdateStringPref(
430 const std::string
& pref_name
,
431 const std::string
& new_value
) {
432 DCHECK(local_store_
.get());
433 if (HasPref(local_store_
, pref_name
) &&
434 GetAndUnpackStringPref(local_store_
, pref_name
) == new_value
) {
437 if (!HasUserSignedOut()) {
438 local_store_
->SetValueSilently(pref_name
, PackCredential(new_value
));
440 // Write a blank value since we cache credentials only for first-time
442 local_store_
->SetValueSilently(pref_name
, PackCredential(std::string()));
444 WriteLastCacheUpdateTime();
447 void CredentialCacheService::UpdateBooleanPref(const std::string
& pref_name
,
449 DCHECK(local_store_
.get());
450 if (HasPref(local_store_
, pref_name
) &&
451 GetBooleanPref(local_store_
, pref_name
) == new_value
) {
454 if (!HasUserSignedOut()) {
455 local_store_
->SetValueSilently(pref_name
,
456 base::Value::CreateBooleanValue(new_value
));
458 // Write a default value of false since we cache credentials only for
459 // first-time sign-ins.
460 local_store_
->SetValueSilently(pref_name
,
461 base::Value::CreateBooleanValue(false));
463 WriteLastCacheUpdateTime();
466 base::Time
CredentialCacheService::GetLastCacheUpdateTime(
467 scoped_refptr
<JsonPrefStore
> store
) {
468 DCHECK(HasPref(store
, kLastCacheUpdateTimeKey
));
469 const base::Value
* last_cache_update_time_value
= NULL
;
470 store
->GetValue(kLastCacheUpdateTimeKey
, &last_cache_update_time_value
);
471 DCHECK(last_cache_update_time_value
);
472 std::string last_cache_update_time_string
;
473 last_cache_update_time_value
->GetAsString(&last_cache_update_time_string
);
474 int64 last_cache_update_time_int64
;
475 bool success
= base::StringToInt64(last_cache_update_time_string
,
476 &last_cache_update_time_int64
);
478 return base::Time::FromInternalValue(last_cache_update_time_int64
);
481 bool CredentialCacheService::AlternateCacheIsMoreRecent() {
482 DCHECK(alternate_store_
.get());
483 // If the alternate credential cache doesn't have the "last_cache_update_time"
484 // field, it was written by an older version of chrome, and we therefore
485 // consider the local cache to be more recent.
486 if (!HasPref(alternate_store_
, kLastCacheUpdateTimeKey
))
488 DCHECK(HasPref(local_store_
, kLastCacheUpdateTimeKey
));
489 return GetLastCacheUpdateTime(alternate_store_
) >
490 GetLastCacheUpdateTime(local_store_
);
493 std::string
CredentialCacheService::GetAndUnpackStringPref(
494 scoped_refptr
<JsonPrefStore
> store
,
495 const std::string
& pref_name
) {
496 const base::Value
* pref_value
= NULL
;
497 store
->GetValue(pref_name
, &pref_value
);
498 return UnpackCredential(*pref_value
);
501 bool CredentialCacheService::GetBooleanPref(
502 scoped_refptr
<JsonPrefStore
> store
,
503 const std::string
& pref_name
) {
504 const base::Value
* pref_value
= NULL
;
505 store
->GetValue(pref_name
, &pref_value
);
507 pref_value
->GetAsBoolean(&pref
);
511 CredentialCacheService::LocalStoreObserver::LocalStoreObserver(
512 CredentialCacheService
* service
,
513 scoped_refptr
<JsonPrefStore
> local_store
)
515 local_store_(local_store
) {
516 local_store_
->AddObserver(this);
519 CredentialCacheService::LocalStoreObserver::~LocalStoreObserver() {
520 local_store_
->RemoveObserver(this);
523 void CredentialCacheService::LocalStoreObserver::OnInitializationCompleted(
525 // Note that |succeeded| will be true even if the local cache file wasn't
526 // found, so long as its parent dir (the chrome profile directory) was found.
527 // If |succeeded| is false, it means that the chrome profile directory is
528 // missing. In this case, there's nothing we can do other than DCHECK.
531 // During startup, we do a precautionary write of the google username,
532 // encryption tokens, sync prefs and last cache update time to the local cache
533 // in order to recover from the following cases:
534 // 1) There is no local credential cache, but the user is signed in. This
535 // could happen if a signed-in user restarts chrome after upgrading from
536 // an older version that didn't support credential caching.
537 // 2) There is a local credential cache, but we missed writing sync credential
538 // updates to it in the past due to a crash, or due to the user exiting
539 // chrome in the midst of a sign in, sign out or reconfigure.
540 // 3) There is a local credential cache that was written to by an older
541 // version of Chrome, and it does not contain the "last_cache_update_time"
543 // Note: If the local credential cache was already up-to-date, the operations
544 // below will be no-ops, and won't change the cache's last updated time. Also,
545 // if the user is not signed in and there is no local credential cache, we
546 // don't want to create a cache with empty values.
547 SigninManager
* signin
=
548 SigninManagerFactory::GetForProfile(service_
->profile_
);
549 if ((local_store_
->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NO_FILE
&&
550 !signin
->GetAuthenticatedUsername().empty()) ||
551 (local_store_
->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE
)) {
552 service_
->PackAndUpdateStringPref(prefs::kGoogleServicesUsername
,
553 signin
->GetAuthenticatedUsername());
554 if (!service_
->sync_prefs_
.GetEncryptionBootstrapToken().empty()) {
555 service_
->PackAndUpdateStringPref(
556 prefs::kSyncEncryptionBootstrapToken
,
557 service_
->sync_prefs_
.GetEncryptionBootstrapToken());
559 if (!service_
->sync_prefs_
.GetKeystoreEncryptionBootstrapToken().empty()) {
560 service_
->PackAndUpdateStringPref(
561 prefs::kSyncKeystoreEncryptionBootstrapToken
,
562 service_
->sync_prefs_
.GetKeystoreEncryptionBootstrapToken());
564 service_
->WriteSyncPrefsToLocalCache();
565 if (!service_
->HasPref(local_store_
, kLastCacheUpdateTimeKey
))
566 service_
->WriteLastCacheUpdateTime();
567 if (service_
->HasPref(local_store_
, kLastUpdatedTimeTicksDeprecated
))
568 local_store_
->RemoveValue(kLastUpdatedTimeTicksDeprecated
);
571 // Now that the local credential cache is ready, start listening for events
572 // associated with various sync config changes.
573 service_
->StartListeningForSyncConfigChanges();
576 void CredentialCacheService::LocalStoreObserver::OnPrefValueChanged(
577 const std::string
& key
) {
578 // Nothing to do here, since credentials are cached silently.
581 CredentialCacheService::AlternateStoreObserver::AlternateStoreObserver(
582 CredentialCacheService
* service
,
583 scoped_refptr
<JsonPrefStore
> alternate_store
)
585 alternate_store_(alternate_store
) {
586 alternate_store_
->AddObserver(this);
589 CredentialCacheService::AlternateStoreObserver::~AlternateStoreObserver() {
590 alternate_store_
->RemoveObserver(this);
593 void CredentialCacheService::AlternateStoreObserver::OnInitializationCompleted(
595 // If an alternate credential cache was found, begin consuming its contents.
596 // If not, schedule a future read.
598 alternate_store_
->GetReadError() == JsonPrefStore::PREF_READ_ERROR_NONE
) {
599 service_
->ReadCachedCredentialsFromAlternateProfile();
601 service_
->ScheduleNextReadFromAlternateCredentialCache(
602 kCredentialCachePollIntervalSecs
);
606 void CredentialCacheService::AlternateStoreObserver::OnPrefValueChanged(
607 const std::string
& key
) {
608 // Nothing to do here, since credentials are cached silently.
611 FilePath
CredentialCacheService::GetCredentialPathInCurrentProfile() const {
612 // The sync credential path in the default Desktop profile is
613 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while
614 // the sync credential path in the default Metro profile is
615 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials".
617 return profile_
->GetPath().Append(chrome::kSyncCredentialsFilename
);
620 FilePath
CredentialCacheService::GetCredentialPathInAlternateProfile() const {
622 FilePath alternate_user_data_dir
;
623 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir
);
625 // TODO(rsimha): This code path is to allow for testing in the presence of
626 // strange singleton mode. Delete this block before shipping.
627 // See http://crbug.com/144280.
628 const CommandLine
* command_line
= CommandLine::ForCurrentProcess();
629 if (command_line
->HasSwitch(switches::kEnableSyncCredentialCaching
) &&
630 !CredentialCacheServiceFactory::IsDefaultProfile(profile_
)) {
631 DCHECK(CredentialCacheServiceFactory::IsDefaultAlternateProfileForTest(
633 chrome::GetDefaultUserDataDirectory(&alternate_user_data_dir
);
636 FilePath alternate_default_profile_dir
=
637 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir
);
638 return alternate_default_profile_dir
.Append(chrome::kSyncCredentialsFilename
);
641 void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
642 local_store_
= new JsonPrefStore(
643 GetCredentialPathInCurrentProfile(),
644 content::BrowserThread::GetMessageLoopProxyForThread(
645 content::BrowserThread::FILE));
646 local_store_observer_
= new LocalStoreObserver(this, local_store_
);
647 local_store_
->ReadPrefsAsync(NULL
);
650 void CredentialCacheService::StartListeningForSyncConfigChanges() {
651 // Register for notifications for google sign in and sign out.
653 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT
,
654 content::Source
<Profile
>(profile_
));
656 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
,
657 content::Source
<Profile
>(profile_
));
659 // Register for notifications for sync configuration changes that could occur
660 // during sign in or reconfiguration.
661 ProfileSyncService
* service
=
662 ProfileSyncServiceFactory::GetForProfile(profile_
);
664 chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
,
665 content::Source
<ProfileSyncService
>(service
));
667 chrome::NOTIFICATION_SYNC_CONFIGURE_START
,
668 content::Source
<ProfileSyncService
>(service
));
670 // Register for notifications for updates to the sync encryption tokens, which
671 // are stored in the PrefStore.
672 pref_registrar_
.Init(profile_
->GetPrefs());
673 pref_registrar_
.Add(prefs::kSyncEncryptionBootstrapToken
, this);
674 pref_registrar_
.Add(prefs::kSyncKeystoreEncryptionBootstrapToken
, this);
676 // Register for notifications for updates to lsid and sid, which are stored in
678 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
680 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED
,
681 content::Source
<TokenService
>(token_service
));
683 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED
,
684 content::Source
<TokenService
>(token_service
));
686 chrome::NOTIFICATION_TOKENS_CLEARED
,
687 content::Source
<TokenService
>(token_service
));
690 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
691 // Attempt to read cached credentials from the alternate profile. If no file
692 // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned
693 // after initialization is complete.
694 FilePath path
= GetCredentialPathInAlternateProfile();
695 alternate_store_
= new JsonPrefStore(
697 content::BrowserThread::GetMessageLoopProxyForThread(
698 content::BrowserThread::FILE));
699 alternate_store_observer_
= new AlternateStoreObserver(this,
701 alternate_store_
->ReadPrefsAsync(NULL
);
704 bool CredentialCacheService::HasUserSignedOut() {
705 DCHECK(local_store_
.get());
706 // If HasPref() is false, the user never signed in, since there are no
707 // previously cached credentials. If the kGoogleServicesUsername pref is
708 // empty, it means that the user signed in and subsequently signed out.
709 return HasPref(local_store_
, prefs::kGoogleServicesUsername
) &&
710 GetAndUnpackStringPref(local_store_
,
711 prefs::kGoogleServicesUsername
).empty();
714 void CredentialCacheService::InitiateSignInWithCachedCredentials(
715 const std::string
& google_services_username
,
716 const std::string
& encryption_bootstrap_token
,
717 const std::string
& keystore_encryption_bootstrap_token
,
718 bool keep_everything_synced
,
719 ModelTypeSet preferred_types
) {
720 // Update the google username in the SigninManager and PrefStore. Also update
721 // its value in the local credential cache, since we will not send out
722 // NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL in this case.
723 ProfileSyncService
* service
=
724 ProfileSyncServiceFactory::GetForProfile(profile_
);
725 service
->signin()->SetAuthenticatedUsername(google_services_username
);
726 profile_
->GetPrefs()->SetString(prefs::kGoogleServicesUsername
,
727 google_services_username
);
728 PackAndUpdateStringPref(prefs::kGoogleServicesUsername
,
729 service
->signin()->GetAuthenticatedUsername());
731 // Update sync encryption tokens after making sure at least one of them is
733 DCHECK(!encryption_bootstrap_token
.empty() ||
734 !keystore_encryption_bootstrap_token
.empty());
735 if (!encryption_bootstrap_token
.empty()) {
736 sync_prefs_
.SetEncryptionBootstrapToken(encryption_bootstrap_token
);
738 if (!keystore_encryption_bootstrap_token
.empty()) {
739 sync_prefs_
.SetKeystoreEncryptionBootstrapToken(
740 keystore_encryption_bootstrap_token
);
743 // Update the sync preferences.
744 sync_prefs_
.SetStartSuppressed(false);
745 sync_prefs_
.SetSyncSetupCompleted();
746 sync_prefs_
.SetKeepEverythingSynced(keep_everything_synced
);
747 sync_prefs_
.SetPreferredDataTypes(service
->GetRegisteredDataTypes(),
751 void CredentialCacheService::UpdateTokenServiceCredentials(
752 const std::string
& alternate_lsid
,
753 const std::string
& alternate_sid
) {
754 GaiaAuthConsumer::ClientLoginResult login_result
;
755 login_result
.lsid
= alternate_lsid
;
756 login_result
.sid
= alternate_sid
;
757 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
758 token_service
->UpdateCredentials(login_result
);
759 DCHECK(token_service
->AreCredentialsValid());
760 token_service
->StartFetchingTokens();
763 void CredentialCacheService::InitiateSignOut() {
764 ProfileSyncService
* service
=
765 ProfileSyncServiceFactory::GetForProfile(profile_
);
766 service
->DisableForUser();
769 bool CredentialCacheService::HaveSyncPrefsChanged(
770 bool alternate_keep_everything_synced
,
771 ModelTypeSet alternate_preferred_types
) const {
772 if (alternate_keep_everything_synced
&&
773 sync_prefs_
.HasKeepEverythingSynced()) {
776 ProfileSyncService
* service
=
777 ProfileSyncServiceFactory::GetForProfile(profile_
);
778 ModelTypeSet local_preferred_types
=
779 sync_prefs_
.GetPreferredDataTypes(service
->GetRegisteredDataTypes());
781 (alternate_keep_everything_synced
!=
782 sync_prefs_
.HasKeepEverythingSynced()) ||
783 !alternate_preferred_types
.Equals(local_preferred_types
);
786 bool CredentialCacheService::HaveSyncEncryptionTokensChanged(
787 const std::string
& alternate_encryption_bootstrap_token
,
788 const std::string
& alternate_keystore_encryption_bootstrap_token
) {
789 std::string local_encryption_bootstrap_token
;
790 if (HasPref(local_store_
, prefs::kSyncEncryptionBootstrapToken
)) {
791 local_encryption_bootstrap_token
=
792 GetAndUnpackStringPref(local_store_
,
793 prefs::kSyncEncryptionBootstrapToken
);
795 std::string local_keystore_encryption_bootstrap_token
;
796 if (HasPref(local_store_
, prefs::kSyncKeystoreEncryptionBootstrapToken
)) {
797 local_keystore_encryption_bootstrap_token
=
798 GetAndUnpackStringPref(local_store_
,
799 prefs::kSyncKeystoreEncryptionBootstrapToken
);
801 return (local_encryption_bootstrap_token
!=
802 alternate_encryption_bootstrap_token
) ||
803 (local_keystore_encryption_bootstrap_token
!=
804 alternate_keystore_encryption_bootstrap_token
);
807 bool CredentialCacheService::HaveTokenServiceCredentialsChanged(
808 const std::string
& alternate_lsid
,
809 const std::string
& alternate_sid
) {
810 std::string local_lsid
=
811 GetAndUnpackStringPref(local_store_
, GaiaConstants::kGaiaLsid
);
812 std::string local_sid
=
813 GetAndUnpackStringPref(local_store_
, GaiaConstants::kGaiaSid
);
814 return local_lsid
!= alternate_lsid
|| local_sid
!= alternate_sid
;
817 bool CredentialCacheService::ShouldSignOutOfSync(
818 const std::string
& alternate_google_services_username
) {
819 // We must sign out of sync iff:
820 // 1) The user is signed in to the local profile.
821 // 2) The user has never signed out of the local profile in the past.
822 // 3) We noticed that the user has signed out of the alternate profile.
823 // 4) The user is not already in the process of configuring sync.
824 // 5) The alternate cache was updated more recently than the local cache.
825 ProfileSyncService
* service
=
826 ProfileSyncServiceFactory::GetForProfile(profile_
);
827 return !service
->signin()->GetAuthenticatedUsername().empty() &&
828 !HasUserSignedOut() &&
829 alternate_google_services_username
.empty() &&
830 !service
->setup_in_progress() &&
831 AlternateCacheIsMoreRecent();
834 bool CredentialCacheService::MayReconfigureSync(
835 const std::string
& alternate_google_services_username
) {
836 // We may attempt to reconfigure sync iff:
837 // 1) The user is signed in to the local profile.
838 // 2) The user has never signed out of the local profile in the past.
839 // 3) The user is signed in to the alternate profile with the same account.
840 // 4) The user is not already in the process of configuring sync.
841 // 5) The alternate cache was updated more recently than the local cache.
842 // 6) The sync backend is initialized and ready to consume config changes.
843 ProfileSyncService
* service
=
844 ProfileSyncServiceFactory::GetForProfile(profile_
);
845 return !service
->signin()->GetAuthenticatedUsername().empty() &&
846 !HasUserSignedOut() &&
847 (alternate_google_services_username
==
848 service
->signin()->GetAuthenticatedUsername()) &&
849 !service
->setup_in_progress() &&
850 AlternateCacheIsMoreRecent() &&
851 service
->ShouldPushChanges();
854 bool CredentialCacheService::ShouldSignInToSync(
855 const std::string
& alternate_google_services_username
,
856 const std::string
& alternate_lsid
,
857 const std::string
& alternate_sid
,
858 const std::string
& alternate_encryption_bootstrap_token
,
859 const std::string
& alternate_keystore_encryption_bootstrap_token
) {
860 // We should sign in with cached credentials from the alternate profile iff:
861 // 1) The user is not currently signed in to the local profile.
862 // 2) The user has never signed out of the local profile in the past.
863 // 3) Valid cached credentials are available in the alternate profile.
864 // 4) The user is not already in the process of configuring sync.
865 ProfileSyncService
* service
=
866 ProfileSyncServiceFactory::GetForProfile(profile_
);
867 return service
->signin()->GetAuthenticatedUsername().empty() &&
868 !HasUserSignedOut() &&
869 !alternate_google_services_username
.empty() &&
870 !alternate_lsid
.empty() &&
871 !alternate_sid
.empty() &&
872 !(alternate_encryption_bootstrap_token
.empty() &&
873 alternate_keystore_encryption_bootstrap_token
.empty()) &&
874 !service
->setup_in_progress();
877 } // namespace syncer