password_syncable_service.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
17namespace password_manager {
18
19namespace {
20
21// Describes the result of merging sync and local passwords.
22enum 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|.
31MergeResult 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
69std::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
81std::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
89syncer::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
103void 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
112PasswordSyncableService::PasswordSyncableService(
113    PasswordStoreSync* password_store)
114    : password_store_(password_store),
115      is_processing_sync_changes_(false) {
116}
117
118PasswordSyncableService::~PasswordSyncableService() {}
119
120syncer::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
191void 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
199syncer::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
214syncer::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
261void 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
291void PasswordSyncableService::InjectStartSyncFlare(
292    const syncer::SyncableService::StartSyncFlare& flare) {
293  DCHECK(CalledOnValidThread());
294  flare_ = flare;
295}
296
297bool 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
324void 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
355void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
356    const PasswordStoreChangeList& changes) {
357  password_store_->NotifyLoginsChanged(changes);
358}
359
360void 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
409syncer::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
438void 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
461std::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