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

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: Fix TokenServiceTests 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 "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/signin/signin_manager.h"
17 #include "chrome/browser/signin/token_service.h"
18 #include "chrome/browser/signin/token_service_factory.h"
19 #include "chrome/browser/sync/glue/chrome_encryptor.h"
20 #include "chrome/browser/sync/profile_sync_service.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/browser/sync/sync_prefs.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 loaded_credentials_(false),
41 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
42 if (profile_) {
43 // |profile_| is null for unit tests.
44 InitializeLocalCredentialCacheWriter();
45 }
46 }
47
48 CredentialCacheService::~CredentialCacheService() {
49 Shutdown();
50 }
51
52 void CredentialCacheService::Shutdown() {
53 if (local_store_)
54 local_store_->RemoveObserver(this);
55 if (alternate_store_)
56 alternate_store_->RemoveObserver(this);
Roger Tawa OOO till Jul 10th 2012/07/23 15:10:33 Should null out the pointers after dealing with th
Raghu Simha 2012/07/23 20:35:54 Good point. Done.
57 }
58
59 // static
60 bool CredentialCacheService::IsDefaultProfileDir(const FilePath& profile_dir) {
61 FilePath default_user_data_dir;
62 chrome::GetDefaultUserDataDirectory(&default_user_data_dir);
63 return profile_dir ==
64 ProfileManager::GetDefaultProfileDir(default_user_data_dir);
65 }
66
67 // static
68 base::StringValue* CredentialCacheService::PackCredential(
69 const std::string& credential) {
70 // Do nothing for empty credentials.
71 if (credential.empty())
72 return base::Value::CreateStringValue("");
73
74 browser_sync::ChromeEncryptor encryptor;
75 std::string encrypted;
76 if (!encryptor.EncryptString(credential, &encrypted)) {
77 NOTREACHED();
78 return base::Value::CreateStringValue(std::string());
79 }
80
81 std::string encoded;
82 if (!base::Base64Encode(encrypted, &encoded)) {
83 NOTREACHED();
84 return base::Value::CreateStringValue(std::string());
85 }
86
87 return base::Value::CreateStringValue(encoded);
88 }
89
90 // static
91 std::string CredentialCacheService::UnpackCredential(
92 const base::Value& packed) {
93 std::string encoded;
94 if (!packed.GetAsString(&encoded)) {
95 NOTREACHED();
96 return std::string();
97 }
98
99 // Do nothing for empty credentials.
100 if (encoded.empty())
101 return std::string();
102
103 std::string encrypted;
104 if (!base::Base64Decode(encoded, &encrypted)) {
105 NOTREACHED();
106 return std::string();
107 }
108
109 browser_sync::ChromeEncryptor encryptor;
110 std::string unencrypted;
111 if (!encryptor.DecryptString(encrypted, &unencrypted)) {
112 NOTREACHED();
113 return std::string();
114 }
115
116 return unencrypted;
117 }
118
119 void CredentialCacheService::OnInitializationCompleted(bool succeeded) {
120 DCHECK(succeeded);
121 // We observe two JsonPrefStores -- one for the local profile and one for the
122 // alternate profile. When the alternate credential store becomes available,
123 // we begin consuming its cached credentials. There is nothing to do when the
124 // local credential store becomes available, since we automatically get
125 // notified when the user signs in / signs out.
126 if (alternate_store_ && alternate_store_->IsInitializationComplete()) {
127 ReadCachedCredentialsFromAlternateProfile();
Roger Tawa OOO till Jul 10th 2012/07/23 15:10:33 Could this happen twice if local store notifies af
Raghu Simha 2012/07/23 20:35:54 Excellent point. This wasn't a problem during test
128 }
129 }
130
131 void CredentialCacheService::OnPrefValueChanged(const std::string& key) {
132 // Nothing to do here, since credentials are cached silently.
133 }
134
135 namespace {
136
137 // Determines if credentials should be read from the alternate profile based
138 // on the existence of the local and alternate credential files. Returns
139 // true via |result| if there is a credential cache file in the alternate
140 // profile, but there isn't one in the local profile. Returns false otherwise.
141 void ShouldReadFromAlternateCache(
142 const FilePath& credential_path_in_current_profile,
143 const FilePath& credential_path_in_alternate_profile,
144 bool* result) {
145 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
146 DCHECK(result);
147 *result = !file_util::PathExists(credential_path_in_current_profile) &&
148 file_util::PathExists(credential_path_in_alternate_profile);
149 }
150
151 } // namespace
152
153 void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
154 bool* should_initialize = new bool(false);
155 content::BrowserThread::PostTaskAndReply(
156 content::BrowserThread::FILE,
157 FROM_HERE,
158 base::Bind(&ShouldReadFromAlternateCache,
159 GetCredentialPathInCurrentProfile(),
160 GetCredentialPathInAlternateProfile(),
161 should_initialize),
162 base::Bind(
163 &CredentialCacheService::InitializeAlternateCredentialCacheReader,
164 weak_factory_.GetWeakPtr(),
165 base::Owned(should_initialize)));
166 }
167
168 bool CredentialCacheService::SuccessfullyLoadedCredentials() const {
169 return loaded_credentials_;
170 }
171
172 FilePath CredentialCacheService::GetCredentialPathInCurrentProfile() const {
173 // The sync credential path in the default Desktop profile is
174 // "%Appdata%\Local\Google\Chrome\User Data\Default\Sync Credentials", while
175 // the sync credential path in the default Metro profile is
176 // "%Appdata%\Local\Google\Chrome\Metro\User Data\Default\Sync Credentials".
177 return profile_->GetPath().Append(chrome::kSyncCredentialsFilename);
178 }
179
180 FilePath CredentialCacheService::GetCredentialPathInAlternateProfile() const {
181 FilePath alternate_user_data_dir;
182 chrome::GetAlternateUserDataDirectory(&alternate_user_data_dir);
183 FilePath alternate_default_profile_dir =
184 ProfileManager::GetDefaultProfileDir(alternate_user_data_dir);
185 return alternate_default_profile_dir.Append(chrome::kSyncCredentialsFilename);
186 }
187
188 void CredentialCacheService::InitializeLocalCredentialCacheWriter() {
189 local_store_ = new JsonPrefStore(
190 GetCredentialPathInCurrentProfile(),
191 content::BrowserThread::GetMessageLoopProxyForThread(
192 content::BrowserThread::FILE));
193 local_store_->AddObserver(this);
194 local_store_->ReadPrefsAsync(NULL);
195
196 // Register for notifications for updates to the sync credentials, which are
197 // stored in the PrefStore.
198 pref_registrar_.Init(profile_->GetPrefs());
199 pref_registrar_.Add(prefs::kSyncEncryptionBootstrapToken, this);
200 pref_registrar_.Add(prefs::kGoogleServicesUsername, this);
201 pref_registrar_.Add(prefs::kSyncKeepEverythingSynced, this);
202 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
203 if (i == NIGORI) // The NIGORI preference is not persisted.
204 continue;
205 pref_registrar_.Add(
206 browser_sync::SyncPrefs::GetPrefNameForDataType(ModelTypeFromInt(i)),
207 this);
208 }
209
210 // Register for notifications for updates to lsid and sid, which are stored in
211 // the TokenService.
212 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
213 registrar_.Add(this,
214 chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
215 content::Source<TokenService>(token_service));
216 registrar_.Add(this,
217 chrome::NOTIFICATION_TOKENS_CLEARED,
218 content::Source<TokenService>(token_service));
219 }
220
221 void CredentialCacheService::InitializeAlternateCredentialCacheReader(
222 bool* should_initialize) {
223 // If |should_initialize| is false, there was no credential cache in the
224 // alternate profile directory, and there is nothing to do.
225 // TODO(rsimha): Add a polling mechanism that periodically examines the
226 // credential file in the alternate profile directory so we can respond to the
227 // user signing in and signing out.
228 DCHECK(should_initialize);
229 if (!*should_initialize)
230 return;
231 alternate_store_ = new JsonPrefStore(
232 GetCredentialPathInAlternateProfile(),
233 content::BrowserThread::GetMessageLoopProxyForThread(
234 content::BrowserThread::FILE));
235 alternate_store_->AddObserver(this);
236 alternate_store_->ReadPrefsAsync(NULL);
237 }
238
239 bool CredentialCacheService::HasPref(scoped_refptr<JsonPrefStore> store,
240 const std::string& pref_name) {
241 return (store->GetValue(pref_name, NULL) == PrefStore::READ_OK);
242 }
243
244 std::string CredentialCacheService::GetStringPref(
Roger Tawa OOO till Jul 10th 2012/07/23 15:10:33 nit: would it be clearer if this was called GetAnd
Raghu Simha 2012/07/23 20:35:54 If we do this, we'll also have to rename UpdateStr
245 scoped_refptr<JsonPrefStore> store,
246 const std::string& pref_name) {
247 const base::Value* pref_value = NULL;
248 store->GetValue(pref_name, &pref_value);
249 return UnpackCredential(*pref_value);
250 }
251
252 bool CredentialCacheService::GetBooleanPref(
253 scoped_refptr<JsonPrefStore> store,
254 const std::string& pref_name) {
255 const base::Value* pref_value = NULL;
256 store->GetValue(pref_name, &pref_value);
257 bool pref;
258 pref_value->GetAsBoolean(&pref);
259 return pref;
260 }
261
262 void CredentialCacheService::ReadCachedCredentialsFromAlternateProfile() {
263 if (!HasPref(alternate_store_, prefs::kGoogleServicesUsername) ||
264 !HasPref(alternate_store_, GaiaConstants::kGaiaLsid) ||
265 !HasPref(alternate_store_, GaiaConstants::kGaiaSid) ||
266 !HasPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken) ||
267 !HasPref(alternate_store_, prefs::kSyncKeepEverythingSynced)) {
268 VLOG(1) << "Could not find cached credentials.";
269 return;
270 }
271
272 std::string google_services_username =
273 GetStringPref(alternate_store_, prefs::kGoogleServicesUsername);
274 std::string lsid =
275 GetStringPref(alternate_store_, GaiaConstants::kGaiaLsid);
276 std::string sid =
277 GetStringPref(alternate_store_, GaiaConstants::kGaiaSid);
278 std::string encryption_bootstrap_token =
279 GetStringPref(alternate_store_, prefs::kSyncEncryptionBootstrapToken);
280 bool keep_everything_synced =
281 GetBooleanPref(alternate_store_, prefs::kSyncKeepEverythingSynced);
282
283 if (google_services_username.empty() ||
284 lsid.empty() ||
285 sid.empty() ||
286 encryption_bootstrap_token.empty()) {
287 VLOG(1) << "Found empty cached credentials.";
288 return;
289 }
290
291 bool datatype_prefs[MODEL_TYPE_COUNT] = { false };
292 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
293 if (i == NIGORI) // The NIGORI preference is not persisted.
294 continue;
295 std::string datatype_pref_name =
296 browser_sync::SyncPrefs::GetPrefNameForDataType(ModelTypeFromInt(i));
297 if (!HasPref(alternate_store_, datatype_pref_name)) {
298 VLOG(1) << "Could not find cached datatype prefs.";
299 return;
300 }
301 datatype_prefs[i] = GetBooleanPref(alternate_store_, datatype_pref_name);
302 }
303
304 ApplyCachedCredentials(google_services_username,
305 lsid,
306 sid,
307 encryption_bootstrap_token,
308 keep_everything_synced,
309 datatype_prefs);
310 }
311
312 void CredentialCacheService::ApplyCachedCredentials(
313 const std::string& google_services_username,
314 const std::string& lsid,
315 const std::string& sid,
316 const std::string& encryption_bootstrap_token,
317 bool keep_everything_synced,
318 const bool datatype_prefs[]) {
319 ProfileSyncService* service =
320 ProfileSyncServiceFactory::GetForProfile(profile_);
321 service->signin()->SetAuthenticatedUsername(google_services_username);
322 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
323 google_services_username);
324 browser_sync::SyncPrefs sync_prefs(profile_->GetPrefs());
325 sync_prefs.SetStartSuppressed(false);
326 sync_prefs.SetSyncSetupCompleted();
327 sync_prefs.SetEncryptionBootstrapToken(encryption_bootstrap_token);
328 sync_prefs.SetKeepEverythingSynced(keep_everything_synced);
329 syncer::ModelTypeSet registered_types;
330 syncer::ModelTypeSet preferred_types;
331 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
332 if (i == NIGORI) // The NIGORI preference is not persisted.
333 continue;
334 registered_types.Put(ModelTypeFromInt(i));
335 if (datatype_prefs[i])
336 preferred_types.Put(ModelTypeFromInt(i));
337 }
338 sync_prefs.SetPreferredDataTypes(registered_types, preferred_types);
339
340 GaiaAuthConsumer::ClientLoginResult login_result;
341 login_result.lsid = lsid;
342 login_result.sid = sid;
343 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
344 token_service->UpdateCredentials(login_result);
345 DCHECK(token_service->AreCredentialsValid());
346 token_service->StartFetchingTokens();
347 }
348
349 bool CredentialCacheService::HasUserSignedOut() {
350 // If HasPref() is false, the user never signed in, since there are no
351 // previously cached credentials. If the kGoogleServicesUsername pref is
352 // empty, it means that the user signed in and subsequently signed out.
353 return HasPref(local_store_, prefs::kGoogleServicesUsername) &&
354 GetStringPref(local_store_, prefs::kGoogleServicesUsername).empty();
355 }
356
357 void CredentialCacheService::UpdateStringPref(const std::string& pref_name,
358 const std::string& new_value) {
359 if (!HasUserSignedOut()) {
360 local_store_->SetValueSilently(pref_name, PackCredential(new_value));
361 } else {
362 // Write a blank value since we only use cached credentials for first-time
363 // sign-ins.
364 local_store_->SetValueSilently(pref_name, PackCredential(std::string()));
365 }
366 }
367
368 void CredentialCacheService::UpdateBooleanPref(const std::string& pref_name,
369 bool new_value) {
370 if (!HasUserSignedOut()) {
371 local_store_->SetValueSilently(pref_name,
372 base::Value::CreateBooleanValue(new_value));
373 } else {
374 // Write a default value of false since we only use cached credentials for
375 // first-time sign-ins.
376 local_store_->SetValueSilently(pref_name,
377 base::Value::CreateBooleanValue(false));
378 }
379 }
380
381 void CredentialCacheService::Observe(
382 int type,
383 const content::NotificationSource& source,
384 const content::NotificationDetails& details) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 DCHECK(local_store_);
387 switch (type) {
388 case chrome::NOTIFICATION_PREF_CHANGED: {
389 const std::string pref_name =
390 *(content::Details<const std::string>(details).ptr());
391 if (pref_name == prefs::kGoogleServicesUsername ||
392 pref_name == prefs::kSyncEncryptionBootstrapToken) {
393 UpdateStringPref(pref_name,
394 profile_->GetPrefs()->GetString(pref_name.c_str()));
395 } else {
396 UpdateBooleanPref(pref_name,
397 profile_->GetPrefs()->GetBoolean(pref_name.c_str()));
398 }
399 break;
400 }
401
402 case chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED: {
403 const TokenService::TokenAvailableDetails& token_details =
404 *(content::Details<const TokenService::TokenAvailableDetails>(
405 details).ptr());
406 if (token_details.service() == GaiaConstants::kGaiaLsid ||
407 token_details.service() == GaiaConstants::kGaiaSid) {
408 UpdateStringPref(token_details.service(), token_details.token());
409 }
410 break;
411 }
412
413 case chrome::NOTIFICATION_TOKENS_CLEARED: {
414 UpdateStringPref(GaiaConstants::kGaiaLsid, std::string());
415 UpdateStringPref(GaiaConstants::kGaiaSid, std::string());
416 break;
417 }
418
419 default: {
420 NOTREACHED();
421 break;
422 }
423 }
424 }
425
426 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698