• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/password_manager/core/browser/password_syncable_service.h"
6 
7 #include "base/auto_reset.h"
8 #include "base/location.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "components/autofill/core/common/password_form.h"
13 #include "components/password_manager/core/browser/password_store_sync.h"
14 #include "net/base/escape.h"
15 #include "sync/api/sync_error_factory.h"
16 
17 namespace password_manager {
18 
19 namespace {
20 
21 // Describes the result of merging sync and local passwords.
22 enum MergeResult {
23   IDENTICAL,
24   SYNC,
25   LOCAL,
26 };
27 
28 // Merges the local and sync passwords and outputs the entry into
29 // |new_password_form|. Returns MergeResult value describing the content of
30 // |new_password_form|.
MergeLocalAndSyncPasswords(const sync_pb::PasswordSpecificsData & password_specifics,const autofill::PasswordForm & password_form,autofill::PasswordForm * new_password_form)31 MergeResult MergeLocalAndSyncPasswords(
32     const sync_pb::PasswordSpecificsData& password_specifics,
33     const autofill::PasswordForm& password_form,
34     autofill::PasswordForm* new_password_form) {
35   DCHECK(new_password_form);
36   if (password_form.scheme == password_specifics.scheme() &&
37       password_form.signon_realm == password_specifics.signon_realm() &&
38       password_form.origin.spec() == password_specifics.origin() &&
39       password_form.action.spec() == password_specifics.action() &&
40       base::UTF16ToUTF8(password_form.username_element) ==
41           password_specifics.username_element() &&
42       base::UTF16ToUTF8(password_form.password_element) ==
43           password_specifics.password_element() &&
44       base::UTF16ToUTF8(password_form.username_value) ==
45           password_specifics.username_value() &&
46       base::UTF16ToUTF8(password_form.password_value) ==
47           password_specifics.password_value() &&
48       password_form.ssl_valid == password_specifics.ssl_valid() &&
49       password_form.preferred == password_specifics.preferred() &&
50       password_form.date_created.ToInternalValue() ==
51           password_specifics.date_created() &&
52       password_form.blacklisted_by_user == password_specifics.blacklisted() &&
53       password_form.type == password_specifics.type() &&
54       password_form.times_used == password_specifics.times_used()) {
55     return IDENTICAL;
56   }
57 
58   // If the passwords differ, take the one that was created more recently.
59   if (base::Time::FromInternalValue(password_specifics.date_created()) <
60           password_form.date_created) {
61     *new_password_form = password_form;
62     return LOCAL;
63   }
64 
65   PasswordFromSpecifics(password_specifics, new_password_form);
66   return SYNC;
67 }
68 
MakePasswordSyncTag(const std::string & origin_url,const std::string & username_element,const std::string & username_value,const std::string & password_element,const std::string & signon_realm)69 std::string MakePasswordSyncTag(const std::string& origin_url,
70                                 const std::string& username_element,
71                                 const std::string& username_value,
72                                 const std::string& password_element,
73                                 const std::string& signon_realm) {
74   return net::EscapePath(origin_url) + "|" +
75          net::EscapePath(username_element) + "|" +
76          net::EscapePath(username_value) + "|" +
77          net::EscapePath(password_element) + "|" +
78          net::EscapePath(signon_realm);
79 }
80 
MakePasswordSyncTag(const autofill::PasswordForm & password)81 std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
82   return MakePasswordSyncTag(password.origin.spec(),
83                              base::UTF16ToUTF8(password.username_element),
84                              base::UTF16ToUTF8(password.username_value),
85                              base::UTF16ToUTF8(password.password_element),
86                              password.signon_realm);
87 }
88 
GetSyncChangeType(PasswordStoreChange::Type type)89 syncer::SyncChange::SyncChangeType GetSyncChangeType(
90     PasswordStoreChange::Type type) {
91   switch (type) {
92     case PasswordStoreChange::ADD:
93       return syncer::SyncChange::ACTION_ADD;
94     case PasswordStoreChange::UPDATE:
95       return syncer::SyncChange::ACTION_UPDATE;
96     case PasswordStoreChange::REMOVE:
97       return syncer::SyncChange::ACTION_DELETE;
98   }
99   NOTREACHED();
100   return syncer::SyncChange::ACTION_INVALID;
101 }
102 
AppendChanges(const PasswordStoreChangeList & new_changes,PasswordStoreChangeList * all_changes)103 void AppendChanges(const PasswordStoreChangeList& new_changes,
104                    PasswordStoreChangeList* all_changes) {
105   all_changes->insert(all_changes->end(),
106                       new_changes.begin(),
107                       new_changes.end());
108 }
109 
110 }  // namespace
111 
PasswordSyncableService(PasswordStoreSync * password_store)112 PasswordSyncableService::PasswordSyncableService(
113     PasswordStoreSync* password_store)
114     : password_store_(password_store),
115       is_processing_sync_changes_(false) {
116 }
117 
~PasswordSyncableService()118 PasswordSyncableService::~PasswordSyncableService() {}
119 
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,scoped_ptr<syncer::SyncChangeProcessor> sync_processor,scoped_ptr<syncer::SyncErrorFactory> sync_error_factory)120 syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
121     syncer::ModelType type,
122     const syncer::SyncDataList& initial_sync_data,
123     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
124     scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
125   DCHECK(CalledOnValidThread());
126   DCHECK_EQ(syncer::PASSWORDS, type);
127   base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
128   syncer::SyncMergeResult merge_result(type);
129   sync_error_factory_ = sync_error_factory.Pass();
130   sync_processor_ = sync_processor.Pass();
131 
132   // We add all the db entries as |new_local_entries| initially. During model
133   // association entries that match a sync entry will be removed and this list
134   // will only contain entries that are not in sync.
135   ScopedVector<autofill::PasswordForm> password_entries;
136   PasswordEntryMap new_local_entries;
137   if (!ReadFromPasswordStore(&password_entries, &new_local_entries)) {
138     DCHECK(sync_error_factory_);
139     merge_result.set_error(sync_error_factory_->CreateAndUploadError(
140         FROM_HERE,
141         "Failed to get passwords from store."));
142     return merge_result;
143   }
144 
145   merge_result.set_num_items_before_association(new_local_entries.size());
146 
147   // List that contains the entries that are known only to sync.
148   ScopedVector<autofill::PasswordForm> new_sync_entries;
149 
150   // List that contains the entries that are known to both sync and db but
151   // have updates in sync. They need to be updated in the passwords db.
152   ScopedVector<autofill::PasswordForm> updated_sync_entries;
153 
154   // Changes from password db that need to be propagated to sync.
155   syncer::SyncChangeList updated_db_entries;
156   for (syncer::SyncDataList::const_iterator sync_iter =
157            initial_sync_data.begin();
158        sync_iter != initial_sync_data.end(); ++sync_iter) {
159     CreateOrUpdateEntry(*sync_iter,
160                         &new_local_entries,
161                         &new_sync_entries,
162                         &updated_sync_entries,
163                         &updated_db_entries);
164   }
165 
166   WriteToPasswordStore(new_sync_entries.get(),
167                        updated_sync_entries.get(),
168                        PasswordForms());
169 
170   merge_result.set_num_items_after_association(
171       merge_result.num_items_before_association() + new_sync_entries.size());
172 
173   merge_result.set_num_items_added(new_sync_entries.size());
174 
175   merge_result.set_num_items_modified(updated_sync_entries.size());
176 
177   for (PasswordEntryMap::iterator it = new_local_entries.begin();
178        it != new_local_entries.end();
179        ++it) {
180     updated_db_entries.push_back(
181         syncer::SyncChange(FROM_HERE,
182                            syncer::SyncChange::ACTION_ADD,
183                            SyncDataFromPassword(*it->second)));
184   }
185 
186   merge_result.set_error(
187       sync_processor_->ProcessSyncChanges(FROM_HERE, updated_db_entries));
188   return merge_result;
189 }
190 
StopSyncing(syncer::ModelType type)191 void PasswordSyncableService::StopSyncing(syncer::ModelType type) {
192   DCHECK(CalledOnValidThread());
193   DCHECK_EQ(syncer::PASSWORDS, type);
194 
195   sync_processor_.reset();
196   sync_error_factory_.reset();
197 }
198 
GetAllSyncData(syncer::ModelType type) const199 syncer::SyncDataList PasswordSyncableService::GetAllSyncData(
200     syncer::ModelType type) const {
201   DCHECK(CalledOnValidThread());
202   DCHECK_EQ(syncer::PASSWORDS, type);
203   ScopedVector<autofill::PasswordForm> password_entries;
204   ReadFromPasswordStore(&password_entries, NULL);
205 
206   syncer::SyncDataList sync_data;
207   for (PasswordForms::iterator it = password_entries.begin();
208        it != password_entries.end(); ++it) {
209     sync_data.push_back(SyncDataFromPassword(**it));
210   }
211   return sync_data;
212 }
213 
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & change_list)214 syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
215     const tracked_objects::Location& from_here,
216     const syncer::SyncChangeList& change_list) {
217   DCHECK(CalledOnValidThread());
218   base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
219   // The |db_entries_map| and associated vectors are filled only in case of
220   // update change.
221   ScopedVector<autofill::PasswordForm> new_sync_entries;
222   ScopedVector<autofill::PasswordForm> updated_sync_entries;
223   ScopedVector<autofill::PasswordForm> deleted_entries;
224   base::Time time_now = base::Time::Now();
225 
226   for (syncer::SyncChangeList::const_iterator it = change_list.begin();
227        it != change_list.end();
228        ++it) {
229     const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics();
230     scoped_ptr<autofill::PasswordForm> form(new autofill::PasswordForm);
231     PasswordFromSpecifics(specifics.password().client_only_encrypted_data(),
232                           form.get());
233     switch (it->change_type()) {
234       case syncer::SyncChange::ACTION_ADD: {
235         form->date_synced = time_now;
236         new_sync_entries.push_back(form.release());
237         break;
238       }
239       case syncer::SyncChange::ACTION_UPDATE: {
240         form->date_synced = time_now;
241         updated_sync_entries.push_back(form.release());
242         break;
243       }
244 
245       case syncer::SyncChange::ACTION_DELETE: {
246         deleted_entries.push_back(form.release());
247         break;
248       }
249       case syncer::SyncChange::ACTION_INVALID:
250         return sync_error_factory_->CreateAndUploadError(
251             FROM_HERE,
252             "Failed to process sync changes for passwords datatype.");
253     }
254   }
255   WriteToPasswordStore(new_sync_entries.get(),
256                        updated_sync_entries.get(),
257                        deleted_entries.get());
258   return syncer::SyncError();
259 }
260 
ActOnPasswordStoreChanges(const PasswordStoreChangeList & local_changes)261 void PasswordSyncableService::ActOnPasswordStoreChanges(
262     const PasswordStoreChangeList& local_changes) {
263   DCHECK(CalledOnValidThread());
264 
265   if (!sync_processor_) {
266     if (!flare_.is_null()) {
267       flare_.Run(syncer::PASSWORDS);
268       flare_.Reset();
269     }
270     return;
271   }
272 
273   // ActOnPasswordStoreChanges() can be called from ProcessSyncChanges(). Do
274   // nothing in this case.
275   if (is_processing_sync_changes_)
276     return;
277   syncer::SyncChangeList sync_changes;
278   for (PasswordStoreChangeList::const_iterator it = local_changes.begin();
279        it != local_changes.end();
280        ++it) {
281     syncer::SyncData data = (it->type() == PasswordStoreChange::REMOVE ?
282         syncer::SyncData::CreateLocalDelete(MakePasswordSyncTag(it->form()),
283                                             syncer::PASSWORDS) :
284         SyncDataFromPassword(it->form()));
285     sync_changes.push_back(
286         syncer::SyncChange(FROM_HERE, GetSyncChangeType(it->type()), data));
287   }
288   sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes);
289 }
290 
InjectStartSyncFlare(const syncer::SyncableService::StartSyncFlare & flare)291 void PasswordSyncableService::InjectStartSyncFlare(
292     const syncer::SyncableService::StartSyncFlare& flare) {
293   DCHECK(CalledOnValidThread());
294   flare_ = flare;
295 }
296 
ReadFromPasswordStore(ScopedVector<autofill::PasswordForm> * password_entries,PasswordEntryMap * passwords_entry_map) const297 bool PasswordSyncableService::ReadFromPasswordStore(
298     ScopedVector<autofill::PasswordForm>* password_entries,
299     PasswordEntryMap* passwords_entry_map) const {
300   DCHECK(password_entries);
301   if (!password_store_->FillAutofillableLogins(&password_entries->get()) ||
302       !password_store_->FillBlacklistLogins(&password_entries->get())) {
303     // Password store often fails to load passwords. Track failures with UMA.
304     // (http://crbug.com/249000)
305     UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
306                               ModelTypeToHistogramInt(syncer::PASSWORDS),
307                               syncer::MODEL_TYPE_COUNT);
308     return false;
309   }
310 
311   if (!passwords_entry_map)
312     return true;
313 
314   for (PasswordForms::iterator it = password_entries->begin();
315        it != password_entries->end(); ++it) {
316      autofill::PasswordForm* password_form = *it;
317      passwords_entry_map->insert(
318          std::make_pair(MakePasswordSyncTag(*password_form), password_form));
319   }
320 
321   return true;
322 }
323 
WriteToPasswordStore(const PasswordForms & new_entries,const PasswordForms & updated_entries,const PasswordForms & deleted_entries)324 void PasswordSyncableService::WriteToPasswordStore(
325     const PasswordForms& new_entries,
326     const PasswordForms& updated_entries,
327     const PasswordForms& deleted_entries) {
328   PasswordStoreChangeList changes;
329   for (std::vector<autofill::PasswordForm*>::const_iterator it =
330            new_entries.begin();
331        it != new_entries.end();
332        ++it) {
333     AppendChanges(password_store_->AddLoginImpl(**it), &changes);
334   }
335 
336   for (std::vector<autofill::PasswordForm*>::const_iterator it =
337            updated_entries.begin();
338        it != updated_entries.end();
339        ++it) {
340     AppendChanges(password_store_->UpdateLoginImpl(**it), &changes);
341   }
342 
343   for (std::vector<autofill::PasswordForm*>::const_iterator it =
344            deleted_entries.begin();
345        it != deleted_entries.end();
346        ++it) {
347     AppendChanges(password_store_->RemoveLoginImpl(**it), &changes);
348   }
349 
350   // We have to notify password store observers of the change by hand since
351   // we use internal password store interfaces to make changes synchronously.
352   NotifyPasswordStoreOfLoginChanges(changes);
353 }
354 
NotifyPasswordStoreOfLoginChanges(const PasswordStoreChangeList & changes)355 void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
356     const PasswordStoreChangeList& changes) {
357   password_store_->NotifyLoginsChanged(changes);
358 }
359 
CreateOrUpdateEntry(const syncer::SyncData & data,PasswordEntryMap * umatched_data_from_password_db,ScopedVector<autofill::PasswordForm> * new_sync_entries,ScopedVector<autofill::PasswordForm> * updated_sync_entries,syncer::SyncChangeList * updated_db_entries)360 void PasswordSyncableService::CreateOrUpdateEntry(
361     const syncer::SyncData& data,
362     PasswordEntryMap* umatched_data_from_password_db,
363     ScopedVector<autofill::PasswordForm>* new_sync_entries,
364     ScopedVector<autofill::PasswordForm>* updated_sync_entries,
365     syncer::SyncChangeList* updated_db_entries) {
366   const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
367   const sync_pb::PasswordSpecificsData& password_specifics(
368       specifics.password().client_only_encrypted_data());
369   std::string tag = MakePasswordSyncTag(password_specifics);
370 
371   // Check whether the data from sync is already in the password store.
372   PasswordEntryMap::iterator existing_local_entry_iter =
373       umatched_data_from_password_db->find(tag);
374   base::Time time_now = base::Time::Now();
375   if (existing_local_entry_iter == umatched_data_from_password_db->end()) {
376     // The sync data is not in the password store, so we need to create it in
377     // the password store. Add the entry to the new_entries list.
378     scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
379     new_password->date_synced = time_now;
380     PasswordFromSpecifics(password_specifics, new_password.get());
381     new_sync_entries->push_back(new_password.release());
382   } else {
383     // The entry is in password store. If the entries are not identical, then
384     // the entries need to be merged.
385     scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
386     switch (MergeLocalAndSyncPasswords(password_specifics,
387                                        *existing_local_entry_iter->second,
388                                        new_password.get())) {
389       case IDENTICAL:
390         break;
391       case SYNC:
392         new_password->date_synced = time_now;
393         updated_sync_entries->push_back(new_password.release());
394         break;
395       case LOCAL:
396         updated_db_entries->push_back(
397             syncer::SyncChange(FROM_HERE,
398                                syncer::SyncChange::ACTION_UPDATE,
399                                SyncDataFromPassword(*new_password)));
400         break;
401     }
402     // Remove the entry from the entry map to indicate a match has been found.
403     // Entries that remain in the map at the end of associating all sync entries
404     // will be treated as additions that need to be propagated to sync.
405     umatched_data_from_password_db->erase(existing_local_entry_iter);
406   }
407 }
408 
SyncDataFromPassword(const autofill::PasswordForm & password_form)409 syncer::SyncData SyncDataFromPassword(
410     const autofill::PasswordForm& password_form) {
411   sync_pb::EntitySpecifics password_data;
412   sync_pb::PasswordSpecificsData* password_specifics =
413       password_data.mutable_password()->mutable_client_only_encrypted_data();
414   password_specifics->set_scheme(password_form.scheme);
415   password_specifics->set_signon_realm(password_form.signon_realm);
416   password_specifics->set_origin(password_form.origin.spec());
417   password_specifics->set_action(password_form.action.spec());
418   password_specifics->set_username_element(
419       base::UTF16ToUTF8(password_form.username_element));
420   password_specifics->set_password_element(
421       base::UTF16ToUTF8(password_form.password_element));
422   password_specifics->set_username_value(
423       base::UTF16ToUTF8(password_form.username_value));
424   password_specifics->set_password_value(
425       base::UTF16ToUTF8(password_form.password_value));
426   password_specifics->set_ssl_valid(password_form.ssl_valid);
427   password_specifics->set_preferred(password_form.preferred);
428   password_specifics->set_date_created(
429       password_form.date_created.ToInternalValue());
430   password_specifics->set_blacklisted(password_form.blacklisted_by_user);
431   password_specifics->set_type(password_form.type);
432   password_specifics->set_times_used(password_form.times_used);
433 
434   std::string tag = MakePasswordSyncTag(*password_specifics);
435   return syncer::SyncData::CreateLocalData(tag, tag, password_data);
436 }
437 
PasswordFromSpecifics(const sync_pb::PasswordSpecificsData & password,autofill::PasswordForm * new_password)438 void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password,
439                            autofill::PasswordForm* new_password) {
440   new_password->scheme =
441       static_cast<autofill::PasswordForm::Scheme>(password.scheme());
442   new_password->signon_realm = password.signon_realm();
443   new_password->origin = GURL(password.origin());
444   new_password->action = GURL(password.action());
445   new_password->username_element =
446       base::UTF8ToUTF16(password.username_element());
447   new_password->password_element =
448       base::UTF8ToUTF16(password.password_element());
449   new_password->username_value = base::UTF8ToUTF16(password.username_value());
450   new_password->password_value = base::UTF8ToUTF16(password.password_value());
451   new_password->ssl_valid = password.ssl_valid();
452   new_password->preferred = password.preferred();
453   new_password->date_created =
454       base::Time::FromInternalValue(password.date_created());
455   new_password->blacklisted_by_user = password.blacklisted();
456   new_password->type =
457       static_cast<autofill::PasswordForm::Type>(password.type());
458   new_password->times_used = password.times_used();
459 }
460 
MakePasswordSyncTag(const sync_pb::PasswordSpecificsData & password)461 std::string MakePasswordSyncTag(
462     const sync_pb::PasswordSpecificsData& password) {
463   return MakePasswordSyncTag(password.origin(),
464                              password.username_element(),
465                              password.username_value(),
466                              password.password_element(),
467                              password.signon_realm());
468 }
469 
470 }  // namespace password_manager
471