Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(279)

Side by Side Diff: chrome/browser/sync/credential_cache_service_win.cc

Issue 10656033: [sync] Automatic bootstrapping of Sync on Win 8 from cached credentials (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/sync/credential_cache_service_win.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/base64.h"
10 #include "base/compiler_specific.h"
11 #include "base/file_util.h"
12 #include "base/values.h"
13 #include "base/win/windows_version.h"
14 #include "chrome/browser/prefs/pref_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/signin/signin_manager.h"
18 #include "chrome/browser/signin/token_service.h"
19 #include "chrome/browser/signin/token_service_factory.h"
20 #include "chrome/browser/sync/glue/chrome_encryptor.h"
21 #include "chrome/browser/sync/profile_sync_service.h"
22 #include "chrome/browser/sync/profile_sync_service_factory.h"
23 #include "chrome/common/chrome_constants.h"
24 #include "chrome/common/chrome_notification_types.h"
25 #include "chrome/common/chrome_paths_internal.h"
26 #include "chrome/common/net/gaia/gaia_auth_consumer.h"
27 #include "chrome/common/net/gaia/gaia_constants.h"
28 #include "chrome/common/pref_names.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_source.h"
32 #include "sync/internal_api/public/base/model_type.h"
33
34 namespace syncer {
35
36 using content::BrowserThread;
37
38 CredentialCacheService::CredentialCacheService(Profile* profile)
39 : profile_(profile),
40 // |profile_| is null in unit tests.
41 sync_prefs_(profile_ ? profile_->GetPrefs() : NULL),
42 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
43 if (profile_) {
44 InitializeLocalCredentialCacheWriter();
45 if (ShouldLookForCachedCredentialsInAlternateProfile())
46 LookForCachedCredentialsInAlternateProfile();
47 }
48 }
49
50 CredentialCacheService::~CredentialCacheService() {
51 Shutdown();
52 }
53
54 void CredentialCacheService::Shutdown() {
55 if (local_store_.get()) {
56 local_store_.release();
57 }
58
59 if (alternate_store_.get()) {
60 alternate_store_->RemoveObserver(this);
61 alternate_store_.release();
62 }
63 }
64
65 void CredentialCacheService::OnInitializationCompleted(bool succeeded) {
66 DCHECK(succeeded);
67 // When the alternate credential store becomes available, begin consuming its
68 // cached credentials.
69 if (alternate_store_.get() && alternate_store_->IsInitializationComplete()) {
70 ReadCachedCredentialsFromAlternateProfile();
71 }
72 }
73
74 void CredentialCacheService::OnPrefValueChanged(const std::string& key) {
75 // Nothing to do here, since credentials are cached silently.
76 }
77
78 void CredentialCacheService::Observe(
79 int type,
80 const content::NotificationSource& source,
81 const content::NotificationDetails& details) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83 DCHECK(local_store_.get());
84 switch (type) {
85 case chrome::NOTIFICATION_PREF_CHANGED: {
86 const std::string pref_name =
87 *(content::Details<const std::string>(details).ptr());
88 if (pref_name == prefs::kGoogleServicesUsername ||
89 pref_name == prefs::kSyncEncryptionBootstrapToken) {
90 PackAndUpdateStringPref(
91 pref_name,
92 profile_->GetPrefs()->GetString(pref_name.c_str()));
93 } else {
94 UpdateBooleanPref(pref_name,
95 profile_->GetPrefs()->GetBoolean(pref_name.c_str()));
96 }
97 break;
98 }
99
100 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: {
101 const TokenService::CredentialsUpdatedDetails& token_details =
102 *(content::Details<const TokenService::CredentialsUpdatedDetails>(
103 details).ptr());
104 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, token_details.lsid());
105 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, token_details.sid());
106 break;
107 }
108
109 case chrome::NOTIFICATION_TOKENS_CLEARED: {
110 PackAndUpdateStringPref(GaiaConstants::kGaiaLsid, std::string());
111 PackAndUpdateStringPref(GaiaConstants::kGaiaSid, std::string());
112 break;
113 }
114
115 default: {
116 NOTREACHED();
117 break;
118 }
119 }
120 }
121
122 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store,
123 const std::string& pref_name) {
124 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK);
125 }
126
127 // static
128 base::StringValue* CredentialCacheService::PackCredential(
129 const std::string& credential) {
130 // Do nothing for empty credentials.
131 if (credential.empty())
132 return base::Value::CreateStringValue("");
133
134 browser_sync::ChromeEncryptor encryptor;
135 std::string encrypted;
136 if (!encryptor.EncryptString(credential, &encrypted)) {
137 NOTREACHED();
138 return base::Value::CreateStringValue(std::string());
139 }
140
141 std::string encoded;
142 if (!base::Base64Encode(encrypted, &encoded)) {
143 NOTREACHED();
144 return base::Value::CreateStringValue(std::string());
145 }
146
147 return base::Value::CreateStringValue(encoded);
148 }
149
150 // static
151 std::string CredentialCacheService::UnpackCredential(
152 const base::Value& packed) {
153 std::string encoded;
154 if (!packed.GetAsString(&encoded)) {
155 NOTREACHED();
156 return std::string();
157 }
158
159 // Do nothing for empty credentials.
160 if (encoded.empty())
161 return std::string();
162
163 std::string encrypted;
164 if (!base::Base64Decode(encoded, &encrypted)) {
165 NOTREACHED();
166 return std::string();
167 }
168
169 browser_sync::ChromeEncryptor encryptor;
170 std::string unencrypted;
171 if (!encryptor.DecryptString(encrypted, &unencrypted)) {
172 NOTREACHED();
173 return std::string();
174 }
175
176 return unencrypted;
177 }
178
179 void CredentialCacheService::PackAndUpdateStringPref(
180 const std::string& pref_name,
181 const std::string& new_value) {
182 DCHECK(local_store_.get());
183 if (!HasUserSignedOut()) {
184 local_store_->SetValueSilently(pref_name, PackCredential(new_value));
185 } else {
186 // Write a blank value since we cache credentials only for first-time
187 // sign-ins.
188 local_store_->SetValueSilently(pref_name, PackCredential(std::string()));
189 }
190 }
191
192 void CredentialCacheService::UpdateBooleanPref(const std::string& pref_name,
193 bool new_value) {
194 DCHECK(local_store_.get());
195 if (!HasUserSignedOut()) {
196 local_store_->SetValueSilently(pref_name,
197 base::Value::CreateBooleanValue(new_value));
198 } else {
199 // Write a default value of false since we cache credentials only for
200 // first-time sign-ins.
201 local_store_->SetValueSilently(pref_name,
202 base::Value::CreateBooleanValue(false));
203 }
204 }
205
206 std::string CredentialCacheService::GetAndUnpackStringPref(
207 scoped_refptr<JsonPrefStore> store,
208 const std::string& pref_name) {
209 const base::Value* pref_value = NULL;
210 store->GetValue(pref_name, &pref_value);
211 return UnpackCredential(*pref_value);
212 }
213
214 bool CredentialCacheService::GetBooleanPref(
215 scoped_refptr<JsonPrefStore> store,
216 const std::string& pref_name) {
217 const base::Value* pref_value = NULL;
218 store->GetValue(pref_name, &pref_value);
219 bool pref;
220 pref_value->GetAsBoolean(&pref);
221 return pref;
222 }
223
224 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const {
225 // The sync credential path in the default Desktop profile is
226 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while
227 // the sync credential path in the default Metro profile is
228 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials".
229 DCHECK(profile_);
230 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename);
231 }
232
233 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const {
234 DCHECK(profile_);
235 FilePath alternate_user_data_dir;
236 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir);
237 FilePath alternate_default_profile_dir =
238 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir);
239 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename);
240 }
241
242 bool CredentialCacheService::ShouldLookForCachedCredentialsInAlternateProfile()
243 const {
244 // We must look for credentials in the alternate profile iff the following are
245 // true:
246 // 1) Sync is not disabled by policy.
247 // 2) Sync startup is not suppressed.
248 // 3) No user is currently signed in to sync.
249 DCHECK(profile_);
250 PrefService* prefs = profile_->GetPrefs();
251 DCHECK(prefs);
252 return !sync_prefs_.IsManaged() &&
253 !sync_prefs_.IsStartSuppressed() &&
254 prefs->GetString(prefs::kGoogleServicesUsername).empty();
255 }
256
257 void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
258 local_store_ = new JsonPrefStore(
259 GetCredentialPathInCurrentProfile(),
260 content::BrowserThread::GetMessageLoopProxyForThread(
261 content::BrowserThread::FILE));
262 local_store_->ReadPrefsAsync(NULL);
263
264 // Register for notifications for updates to the sync credentials, which are
265 // stored in the PrefStore.
266 pref_registrar_.Init(profile_->GetPrefs());
267 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this);
268 pref_registrar_.Add(prefs::kGoogleServicesUsername, this);
269 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this);
270 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
271 if (i == NIGORI) // The NIGORI preference is not persisted.
272 continue;
273 pref_registrar_.Add(
274 browser_sync::SyncPrefs::GetPrefNameForDataType(ModelTypeFromInt(i)),
275 this);
276 }
277
278 // Register for notifications for updates to lsid and sid, which are stored in
279 // the TokenService.
280 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
281 registrar_.Add(this,
282 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
283 content::Source<TokenService>(token_service));
284 registrar_.Add(this,
285 chrome::NOTIFICATION_TOKENS_CLEARED,
286 content::Source<TokenService>(token_service));
287 }
288
289 void CredentialCacheService::InitializeAlternateCredentialCacheReader(
290 bool* should_initialize) {
291 // If |should_initialize| is false, there was no credential cache in the
292 // alternate profile directory, and there is nothing to do.
293 // TODO(rsimha): Add a polling mechanism that periodically examines the
294 // credential file in the alternate profile directory so we can respond to the
295 // user signing in and signing out.
296 DCHECK(should_initialize);
297 if (!*should_initialize)
298 return;
299 alternate_store_ = new JsonPrefStore(
300 GetCredentialPathInAlternateProfile(),
301 content::BrowserThread::GetMessageLoopProxyForThread(
302 content::BrowserThread::FILE));
303 alternate_store_->AddObserver(this);
304 alternate_store_->ReadPrefsAsync(NULL);
305 }
306
307 bool CredentialCacheService::HasUserSignedOut() {
308 DCHECK(local_store_.get());
309 // If HasPref() is false, the user never signed in, since there are no
310 // previously cached credentials. If the kGoogleServicesUsername pref is
311 // empty, it means that the user signed in and subsequently signed out.
312 return HasPref(local_store_, prefs::kGoogleServicesUsername) &&
313 GetAndUnpackStringPref(local_store_,
314 prefs::kGoogleServicesUsername).empty();
315 }
316
317 namespace {
318
319 // Determines if credentials should be read from the alternate profile based
320 // on the existence of the local and alternate credential files. Returns
321 // true via |result| if there is a credential cache file in the alternate
322 // profile, but there isn't one in the local profile. Returns false otherwise.
323 void ShouldReadFromAlternateCache(
324 const FilePath& credential_path_in_current_profile,
325 const FilePath& credential_path_in_alternate_profile,
326 bool* result) {
327 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
328 DCHECK(result);
329 *result = !file_util::PathExists(credential_path_in_current_profile) &&
330 file_util::PathExists(credential_path_in_alternate_profile);
331 }
332
333 } // namespace
334
335 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
336 bool* should_initialize = new bool(false);
337 content::BrowserThread::PostTaskAndReply(
338 content::BrowserThread::FILE,
339 FROM_HERE,
340 base::Bind(&ShouldReadFromAlternateCache,
341 GetCredentialPathInCurrentProfile(),
342 GetCredentialPathInAlternateProfile(),
343 should_initialize),
344 base::Bind(
345 &CredentialCacheService::InitializeAlternateCredentialCacheReader,
346 weak_factory_.GetWeakPtr(),
347 base::Owned(should_initialize)));
348 }
349
350 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() {
351 DCHECK(alternate_store_.get());
352 if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) ||
353 !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) ||
354 !HasPref(alternate_store_, GaiaConstants::kGaiaSid) ||
355 !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) ||
356 !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) {
357 VLOG(1) << "Could not find cached credentials.";
358 return;
359 }
360
361 std::string google_services_username =
362 GetAndUnpackStringPref(alternate_store_, prefs::kGoogleServicesUsername);
363 std::string lsid =
364 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaLsid);
365 std::string sid =
366 GetAndUnpackStringPref(alternate_store_, GaiaConstants::kGaiaSid);
367 std::string encryption_bootstrap_token =
368 GetAndUnpackStringPref(alternate_store_,
369 prefs::kSyncEncryptionBootstrapToken);
370 bool keep_everything_synced =
371 GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced);
372
373 if (google_services_username.empty() ||
374 lsid.empty() ||
375 sid.empty() ||
376 encryption_bootstrap_token.empty()) {
377 VLOG(1) << "Found empty cached credentials.";
378 return;
379 }
380
381 bool datatype_prefs[MODEL_TYPE_COUNT] = { false };
382 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
383 if (i == NIGORI) // The NIGORI preference is not persisted.
384 continue;
385 std::string datatype_pref_name =
386 browser_sync::SyncPrefs::GetPrefNameForDataType(ModelTypeFromInt(i));
387 if (!HasPref(alternate_store_, datatype_pref_name)) {
388 VLOG(1) << "Could not find cached datatype prefs.";
389 return;
390 }
391 datatype_prefs[i] = GetBooleanPref(alternate_store_, datatype_pref_name);
392 }
393
394 ApplyCachedCredentials(google_services_username,
395 lsid,
396 sid,
397 encryption_bootstrap_token,
398 keep_everything_synced,
399 datatype_prefs);
400 }
401
402 void CredentialCacheService::ApplyCachedCredentials(
403 const std::string& google_services_username,
404 const std::string& lsid,
405 const std::string& sid,
406 const std::string& encryption_bootstrap_token,
407 bool keep_everything_synced,
408 const bool datatype_prefs[]) {
409 // Update the google username in the SigninManager and PrefStore.
410 ProfileSyncService* service =
411 ProfileSyncServiceFactory::GetForProfile(profile_);
412 service->signin()->SetAuthenticatedUsername(google_services_username);
413 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
414 google_services_username);
415
416 // Update the sync preferences.
417 sync_prefs_.SetStartSuppressed(false);
418 sync_prefs_.SetSyncSetupCompleted();
419 sync_prefs_.SetEncryptionBootstrapToken(encryption_bootstrap_token);
420 sync_prefs_.SetKeepEverythingSynced(keep_everything_synced);
421 syncer::ModelTypeSet registered_types;
422 syncer::ModelTypeSet preferred_types;
423 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
424 if (i == NIGORI) // The NIGORI preference is not persisted.
425 continue;
426 registered_types.Put(ModelTypeFromInt(i));
427 if (datatype_prefs[i])
428 preferred_types.Put(ModelTypeFromInt(i));
429 }
430 sync_prefs_.SetPreferredDataTypes(registered_types, preferred_types);
431
432 // Update the lsid and sid in the TokenService and mint new tokens for all
433 // Chrome services.
434 GaiaAuthConsumer::ClientLoginResult login_result;
435 login_result.lsid = lsid;
436 login_result.sid = sid;
437 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
438 token_service->UpdateCredentials(login_result);
439 DCHECK(token_service->AreCredentialsValid());
440 token_service->StartFetchingTokens();
441 }
442
443 } // namespace syncer
OLDNEW
« no previous file with comments | « chrome/browser/sync/credential_cache_service_win.h ('k') | chrome/browser/sync/credential_cache_service_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698