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