1 // Copyright 2013 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.
5 #include "chrome/browser/password_manager/password_syncable_service.h"
7 #include "base/location.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/autofill/core/common/password_form.h"
12 #include "components/password_manager/core/browser/password_store.h"
13 #include "net/base/escape.h"
14 #include "sync/api/sync_error_factory.h"
18 // Describes the result of merging sync and local passwords.
25 // Merges the local and sync passwords and outputs the entry into
26 // |new_password_form|. Returns MergeResult value describing the content of
27 // |new_password_form|.
28 MergeResult
MergeLocalAndSyncPasswords(
29 const sync_pb::PasswordSpecificsData
& password_specifics
,
30 const autofill::PasswordForm
& password_form
,
31 autofill::PasswordForm
* new_password_form
) {
32 DCHECK(new_password_form
);
33 if (password_form
.scheme
== password_specifics
.scheme() &&
34 password_form
.signon_realm
== password_specifics
.signon_realm() &&
35 password_form
.origin
.spec() == password_specifics
.origin() &&
36 password_form
.action
.spec() == password_specifics
.action() &&
37 base::UTF16ToUTF8(password_form
.username_element
) ==
38 password_specifics
.username_element() &&
39 base::UTF16ToUTF8(password_form
.password_element
) ==
40 password_specifics
.password_element() &&
41 base::UTF16ToUTF8(password_form
.username_value
) ==
42 password_specifics
.username_value() &&
43 base::UTF16ToUTF8(password_form
.password_value
) ==
44 password_specifics
.password_value() &&
45 password_form
.ssl_valid
== password_specifics
.ssl_valid() &&
46 password_form
.preferred
== password_specifics
.preferred() &&
47 password_form
.date_created
.ToInternalValue() ==
48 password_specifics
.date_created() &&
49 password_form
.blacklisted_by_user
== password_specifics
.blacklisted()) {
53 // If the passwords differ, take the one that was created more recently.
54 if (base::Time::FromInternalValue(password_specifics
.date_created()) <=
55 password_form
.date_created
) {
56 *new_password_form
= password_form
;
60 PasswordFromSpecifics(password_specifics
, new_password_form
);
64 std::string
MakePasswordSyncTag(const std::string
& origin_url
,
65 const std::string
& username_element
,
66 const std::string
& username_value
,
67 const std::string
& password_element
,
68 const std::string
& signon_realm
) {
69 return net::EscapePath(origin_url
) + "|" +
70 net::EscapePath(username_element
) + "|" +
71 net::EscapePath(username_value
) + "|" +
72 net::EscapePath(password_element
) + "|" +
73 net::EscapePath(signon_realm
);
76 std::string
MakePasswordSyncTag(const autofill::PasswordForm
& password
) {
77 return MakePasswordSyncTag(password
.origin
.spec(),
78 base::UTF16ToUTF8(password
.username_element
),
79 base::UTF16ToUTF8(password
.username_value
),
80 base::UTF16ToUTF8(password
.password_element
),
81 password
.signon_realm
);
84 syncer::SyncChange::SyncChangeType
GetSyncChangeType(
85 PasswordStoreChange::Type type
) {
87 case PasswordStoreChange::ADD
:
88 return syncer::SyncChange::ACTION_ADD
;
89 case PasswordStoreChange::UPDATE
:
90 return syncer::SyncChange::ACTION_UPDATE
;
91 case PasswordStoreChange::REMOVE
:
92 return syncer::SyncChange::ACTION_DELETE
;
95 return syncer::SyncChange::ACTION_INVALID
;
98 void AppendChanges(const PasswordStoreChangeList
& new_changes
,
99 PasswordStoreChangeList
* all_changes
) {
100 all_changes
->insert(all_changes
->end(),
107 PasswordSyncableService::PasswordSyncableService(
108 scoped_refptr
<PasswordStore
> password_store
)
109 : password_store_(password_store
) {
112 PasswordSyncableService::~PasswordSyncableService() {}
114 syncer::SyncMergeResult
PasswordSyncableService::MergeDataAndStartSyncing(
115 syncer::ModelType type
,
116 const syncer::SyncDataList
& initial_sync_data
,
117 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
118 scoped_ptr
<syncer::SyncErrorFactory
> sync_error_factory
) {
119 DCHECK_EQ(syncer::PASSWORDS
, type
);
120 syncer::SyncMergeResult
merge_result(type
);
121 sync_error_factory_
= sync_error_factory
.Pass();
122 sync_processor_
= sync_processor
.Pass();
124 // We add all the db entries as |new_local_entries| initially. During model
125 // association entries that match a sync entry will be removed and this list
126 // will only contain entries that are not in sync.
127 ScopedVector
<autofill::PasswordForm
> password_entries
;
128 PasswordEntryMap new_local_entries
;
129 if (!ReadFromPasswordStore(&password_entries
, &new_local_entries
)) {
130 DCHECK(sync_error_factory_
);
131 merge_result
.set_error(sync_error_factory_
->CreateAndUploadError(
133 "Failed to get passwords from store."));
137 merge_result
.set_num_items_before_association(new_local_entries
.size());
139 // List that contains the entries that are known only to sync.
140 ScopedVector
<autofill::PasswordForm
> new_sync_entries
;
142 // List that contains the entries that are known to both sync and db but
143 // have updates in sync. They need to be updated in the passwords db.
144 ScopedVector
<autofill::PasswordForm
> updated_sync_entries
;
146 // Changes from password db that need to be propagated to sync.
147 syncer::SyncChangeList updated_db_entries
;
148 for (syncer::SyncDataList::const_iterator sync_iter
=
149 initial_sync_data
.begin();
150 sync_iter
!= initial_sync_data
.end(); ++sync_iter
) {
151 CreateOrUpdateEntry(*sync_iter
,
154 &updated_sync_entries
,
155 &updated_db_entries
);
158 WriteToPasswordStore(new_sync_entries
.get(),
159 updated_sync_entries
.get(),
162 merge_result
.set_num_items_after_association(
163 merge_result
.num_items_before_association() + new_sync_entries
.size());
165 merge_result
.set_num_items_added(new_sync_entries
.size());
167 merge_result
.set_num_items_modified(updated_sync_entries
.size());
169 for (PasswordEntryMap::iterator it
= new_local_entries
.begin();
170 it
!= new_local_entries
.end();
172 updated_db_entries
.push_back(
173 syncer::SyncChange(FROM_HERE
,
174 syncer::SyncChange::ACTION_ADD
,
175 SyncDataFromPassword(*it
->second
)));
178 merge_result
.set_error(
179 sync_processor_
->ProcessSyncChanges(FROM_HERE
, updated_db_entries
));
183 void PasswordSyncableService::StopSyncing(syncer::ModelType type
) {
184 sync_processor_
.reset();
185 sync_error_factory_
.reset();
188 syncer::SyncDataList
PasswordSyncableService::GetAllSyncData(
189 syncer::ModelType type
) const {
190 DCHECK_EQ(syncer::PASSWORDS
, type
);
191 ScopedVector
<autofill::PasswordForm
> password_entries
;
192 ReadFromPasswordStore(&password_entries
, NULL
);
194 syncer::SyncDataList sync_data
;
195 for (PasswordForms::iterator it
= password_entries
.begin();
196 it
!= password_entries
.end(); ++it
) {
197 sync_data
.push_back(SyncDataFromPassword(**it
));
202 syncer::SyncError
PasswordSyncableService::ProcessSyncChanges(
203 const tracked_objects::Location
& from_here
,
204 const syncer::SyncChangeList
& change_list
) {
205 // The |db_entries_map| and associated vectors are filled only in case of
207 PasswordEntryMap db_entries_map
;
208 ScopedVector
<autofill::PasswordForm
> password_db_entries
;
209 ScopedVector
<autofill::PasswordForm
> new_sync_entries
;
210 ScopedVector
<autofill::PasswordForm
> updated_sync_entries
;
211 ScopedVector
<autofill::PasswordForm
> deleted_entries
;
212 bool has_read_passwords
= false;
214 for (syncer::SyncChangeList::const_iterator it
= change_list
.begin();
215 it
!= change_list
.end();
217 switch (it
->change_type()) {
218 case syncer::SyncChange::ACTION_ADD
: {
219 const sync_pb::EntitySpecifics
& specifics
=
220 it
->sync_data().GetSpecifics();
221 new_sync_entries
.push_back(new autofill::PasswordForm
);
222 PasswordFromSpecifics(specifics
.password().client_only_encrypted_data(),
223 new_sync_entries
.back());
226 case syncer::SyncChange::ACTION_UPDATE
: {
227 if (!has_read_passwords
) {
228 if (ReadFromPasswordStore(&password_db_entries
,
230 has_read_passwords
= true;
232 return sync_error_factory_
->CreateAndUploadError(
234 "Failed to get passwords from store.");
237 syncer::SyncChangeList sync_changes
;
239 CreateOrUpdateEntry(it
->sync_data(),
242 &updated_sync_entries
,
244 DCHECK(sync_changes
.empty());
248 case syncer::SyncChange::ACTION_DELETE
: {
249 const sync_pb::EntitySpecifics
& specifics
=
250 it
->sync_data().GetSpecifics();
251 const sync_pb::PasswordSpecificsData
& password_specifics(
252 specifics
.password().client_only_encrypted_data());
253 deleted_entries
.push_back(new autofill::PasswordForm
);
254 PasswordFromSpecifics(password_specifics
, deleted_entries
.back());
257 case syncer::SyncChange::ACTION_INVALID
:
258 return sync_error_factory_
->CreateAndUploadError(
260 "Failed to process sync changes for passwords datatype.");
263 WriteToPasswordStore(new_sync_entries
.get(),
264 updated_sync_entries
.get(),
265 deleted_entries
.get());
266 return syncer::SyncError();
269 void PasswordSyncableService::ActOnPasswordStoreChanges(
270 const PasswordStoreChangeList
& local_changes
) {
271 if (!sync_processor_
)
273 syncer::SyncChangeList sync_changes
;
274 for (PasswordStoreChangeList::const_iterator it
= local_changes
.begin();
275 it
!= local_changes
.end();
277 sync_changes
.push_back(
278 syncer::SyncChange(FROM_HERE
,
279 GetSyncChangeType(it
->type()),
280 SyncDataFromPassword(it
->form())));
282 sync_processor_
->ProcessSyncChanges(FROM_HERE
, sync_changes
);
285 bool PasswordSyncableService::ReadFromPasswordStore(
286 ScopedVector
<autofill::PasswordForm
>* password_entries
,
287 PasswordEntryMap
* passwords_entry_map
) const {
288 DCHECK(password_entries
);
289 if (!password_store_
->FillAutofillableLogins(&password_entries
->get()) ||
290 !password_store_
->FillBlacklistLogins(&password_entries
->get())) {
291 // Password store often fails to load passwords. Track failures with UMA.
292 // (http://crbug.com/249000)
293 UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
294 ModelTypeToHistogramInt(syncer::PASSWORDS
),
295 syncer::MODEL_TYPE_COUNT
);
299 if (!passwords_entry_map
)
302 for (PasswordForms::iterator it
= password_entries
->begin();
303 it
!= password_entries
->end(); ++it
) {
304 autofill::PasswordForm
* password_form
= *it
;
305 passwords_entry_map
->insert(
306 std::make_pair(MakePasswordSyncTag(*password_form
), password_form
));
312 void PasswordSyncableService::WriteToPasswordStore(
313 const PasswordForms
& new_entries
,
314 const PasswordForms
& updated_entries
,
315 const PasswordForms
& deleted_entries
) {
316 PasswordStoreChangeList changes
;
317 for (std::vector
<autofill::PasswordForm
*>::const_iterator it
=
319 it
!= new_entries
.end();
321 AppendChanges(password_store_
->AddLoginImpl(**it
), &changes
);
324 for (std::vector
<autofill::PasswordForm
*>::const_iterator it
=
325 updated_entries
.begin();
326 it
!= updated_entries
.end();
328 AppendChanges(password_store_
->UpdateLoginImpl(**it
), &changes
);
331 for (std::vector
<autofill::PasswordForm
*>::const_iterator it
=
332 deleted_entries
.begin();
333 it
!= deleted_entries
.end();
335 AppendChanges(password_store_
->RemoveLoginImpl(**it
), &changes
);
338 // We have to notify password store observers of the change by hand since
339 // we use internal password store interfaces to make changes synchronously.
340 NotifyPasswordStoreOfLoginChanges(changes
);
343 void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
344 const PasswordStoreChangeList
& changes
) {
345 password_store_
->NotifyLoginsChanged(changes
);
348 void PasswordSyncableService::CreateOrUpdateEntry(
349 const syncer::SyncData
& data
,
350 PasswordEntryMap
* umatched_data_from_password_db
,
351 ScopedVector
<autofill::PasswordForm
>* new_sync_entries
,
352 ScopedVector
<autofill::PasswordForm
>* updated_sync_entries
,
353 syncer::SyncChangeList
* updated_db_entries
) {
354 const sync_pb::EntitySpecifics
& specifics
= data
.GetSpecifics();
355 const sync_pb::PasswordSpecificsData
& password_specifics(
356 specifics
.password().client_only_encrypted_data());
357 std::string tag
= MakePasswordSyncTag(password_specifics
);
359 // Check whether the data from sync is already in the password store.
360 PasswordEntryMap::iterator existing_local_entry_iter
=
361 umatched_data_from_password_db
->find(tag
);
362 if (existing_local_entry_iter
== umatched_data_from_password_db
->end()) {
363 // The sync data is not in the password store, so we need to create it in
364 // the password store. Add the entry to the new_entries list.
365 scoped_ptr
<autofill::PasswordForm
> new_password(new autofill::PasswordForm
);
366 PasswordFromSpecifics(password_specifics
, new_password
.get());
367 new_sync_entries
->push_back(new_password
.release());
369 // The entry is in password store. If the entries are not identical, then
370 // the entries need to be merged.
371 scoped_ptr
<autofill::PasswordForm
> new_password(new autofill::PasswordForm
);
372 switch (MergeLocalAndSyncPasswords(password_specifics
,
373 *existing_local_entry_iter
->second
,
374 new_password
.get())) {
378 updated_sync_entries
->push_back(new_password
.release());
381 updated_db_entries
->push_back(
382 syncer::SyncChange(FROM_HERE
,
383 syncer::SyncChange::ACTION_UPDATE
,
384 SyncDataFromPassword(*new_password
)));
387 // Remove the entry from the entry map to indicate a match has been found.
388 // Entries that remain in the map at the end of associating all sync entries
389 // will be treated as additions that need to be propagated to sync.
390 umatched_data_from_password_db
->erase(existing_local_entry_iter
);
394 syncer::SyncData
SyncDataFromPassword(
395 const autofill::PasswordForm
& password_form
) {
396 sync_pb::EntitySpecifics password_data
;
397 sync_pb::PasswordSpecificsData
* password_specifics
=
398 password_data
.mutable_password()->mutable_client_only_encrypted_data();
399 password_specifics
->set_scheme(password_form
.scheme
);
400 password_specifics
->set_signon_realm(password_form
.signon_realm
);
401 password_specifics
->set_origin(password_form
.origin
.spec());
402 password_specifics
->set_action(password_form
.action
.spec());
403 password_specifics
->set_username_element(
404 base::UTF16ToUTF8(password_form
.username_element
));
405 password_specifics
->set_password_element(
406 base::UTF16ToUTF8(password_form
.password_element
));
407 password_specifics
->set_username_value(
408 base::UTF16ToUTF8(password_form
.username_value
));
409 password_specifics
->set_password_value(
410 base::UTF16ToUTF8(password_form
.password_value
));
411 password_specifics
->set_ssl_valid(password_form
.ssl_valid
);
412 password_specifics
->set_preferred(password_form
.preferred
);
413 password_specifics
->set_date_created(
414 password_form
.date_created
.ToInternalValue());
415 password_specifics
->set_blacklisted(password_form
.blacklisted_by_user
);
417 std::string tag
= MakePasswordSyncTag(*password_specifics
);
418 return syncer::SyncData::CreateLocalData(tag
, tag
, password_data
);
421 void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData
& password
,
422 autofill::PasswordForm
* new_password
) {
423 new_password
->scheme
=
424 static_cast<autofill::PasswordForm::Scheme
>(password
.scheme());
425 new_password
->signon_realm
= password
.signon_realm();
426 new_password
->origin
= GURL(password
.origin());
427 new_password
->action
= GURL(password
.action());
428 new_password
->username_element
=
429 base::UTF8ToUTF16(password
.username_element());
430 new_password
->password_element
=
431 base::UTF8ToUTF16(password
.password_element());
432 new_password
->username_value
= base::UTF8ToUTF16(password
.username_value());
433 new_password
->password_value
= base::UTF8ToUTF16(password
.password_value());
434 new_password
->ssl_valid
= password
.ssl_valid();
435 new_password
->preferred
= password
.preferred();
436 new_password
->date_created
=
437 base::Time::FromInternalValue(password
.date_created());
438 new_password
->blacklisted_by_user
= password
.blacklisted();
441 std::string
MakePasswordSyncTag(
442 const sync_pb::PasswordSpecificsData
& password
) {
443 return MakePasswordSyncTag(password
.origin(),
444 password
.username_element(),
445 password
.username_value(),
446 password
.password_element(),
447 password
.signon_realm());