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

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

Powered by Google App Engine
This is Rietveld 408576698