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