Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: chrome/browser/password_manager/password_syncable_service.cc

Issue 131003006: Password sync refactoring: implemented |MergeDataAndStartSyncing| and unit_tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge with trunk Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/password_manager/password_syncable_service.h" 5 #include "chrome/browser/password_manager/password_syncable_service.h"
6 6
7 #include "base/location.h" 7 #include "base/location.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/metrics/histogram.h"
8 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/password_manager/password_store.h" 11 #include "chrome/browser/password_manager/password_store.h"
10 #include "components/autofill/core/common/password_form.h" 12 #include "components/autofill/core/common/password_form.h"
11 #include "net/base/escape.h" 13 #include "net/base/escape.h"
12 #include "sync/api/sync_error_factory.h" 14 #include "sync/api/sync_error_factory.h"
13 15
14 namespace { 16 namespace {
15 17
18 // Merges the local and sync passwords and outputs the entry into
19 // |new_password_form|. Returns true if the local and the sync passwords differ.
20 // Returns false if they are identical.
21 bool MergeLocalAndSyncPasswords(
22 const sync_pb::PasswordSpecificsData& password_specifics,
23 const autofill::PasswordForm& password_form,
24 autofill::PasswordForm* new_password_form) {
25 DCHECK(new_password_form);
26 if (password_form.scheme == password_specifics.scheme() &&
27 password_form.signon_realm == password_specifics.signon_realm() &&
28 password_form.origin.spec() == password_specifics.origin() &&
29 password_form.action.spec() == password_specifics.action() &&
30 base::UTF16ToUTF8(password_form.username_element) ==
31 password_specifics.username_element() &&
32 base::UTF16ToUTF8(password_form.password_element) ==
33 password_specifics.password_element() &&
34 base::UTF16ToUTF8(password_form.username_value) ==
35 password_specifics.username_value() &&
36 base::UTF16ToUTF8(password_form.password_value) ==
37 password_specifics.password_value() &&
38 password_form.ssl_valid == password_specifics.ssl_valid() &&
39 password_form.preferred == password_specifics.preferred() &&
40 password_form.date_created.ToInternalValue() ==
41 password_specifics.date_created() &&
42 password_form.blacklisted_by_user == password_specifics.blacklisted()) {
43 return false;
44 }
45
46 // If the passwords differ, take the one that was created more recently.
47 if (base::Time::FromInternalValue(password_specifics.date_created()) <=
48 password_form.date_created) {
49 *new_password_form = password_form;
50 } else {
51 PasswordFromSpecifics(password_specifics, new_password_form);
52 }
53
54 return true;
55 }
56
57 std::string MakePasswordSyncTag(const std::string& origin_url,
58 const std::string& username_element,
59 const std::string& username_value,
60 const std::string& password_element,
61 const std::string& signon_realm) {
62 return net::EscapePath(origin_url) + "|" +
63 net::EscapePath(username_element) + "|" +
64 net::EscapePath(username_value) + "|" +
65 net::EscapePath(password_element) + "|" +
66 net::EscapePath(signon_realm);
67 }
68
69 std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
70 return MakePasswordSyncTag(password.origin.spec(),
71 base::UTF16ToUTF8(password.username_element),
72 base::UTF16ToUTF8(password.username_value),
73 base::UTF16ToUTF8(password.password_element),
74 password.signon_realm);
75 }
76
16 syncer::SyncChange::SyncChangeType GetSyncChangeType( 77 syncer::SyncChange::SyncChangeType GetSyncChangeType(
17 PasswordStoreChange::Type type) { 78 PasswordStoreChange::Type type) {
18 switch (type) { 79 switch (type) {
19 case PasswordStoreChange::ADD: 80 case PasswordStoreChange::ADD:
20 return syncer::SyncChange::ACTION_ADD; 81 return syncer::SyncChange::ACTION_ADD;
21 case PasswordStoreChange::UPDATE: 82 case PasswordStoreChange::UPDATE:
22 return syncer::SyncChange::ACTION_UPDATE; 83 return syncer::SyncChange::ACTION_UPDATE;
23 case PasswordStoreChange::REMOVE: 84 case PasswordStoreChange::REMOVE:
24 return syncer::SyncChange::ACTION_DELETE; 85 return syncer::SyncChange::ACTION_DELETE;
25 } 86 }
26 NOTREACHED(); 87 NOTREACHED();
27 return syncer::SyncChange::ACTION_INVALID; 88 return syncer::SyncChange::ACTION_INVALID;
28 } 89 }
29 90
30 void AppendChanges(const PasswordStoreChangeList& new_changes, 91 void AppendChanges(const PasswordStoreChangeList& new_changes,
31 PasswordStoreChangeList* all_changes) { 92 PasswordStoreChangeList* all_changes) {
32 all_changes->insert(all_changes->end(), 93 all_changes->insert(all_changes->end(),
33 new_changes.begin(), 94 new_changes.begin(),
34 new_changes.end()); 95 new_changes.end());
35 } 96 }
36 97
37 } // namespace 98 } // namespace
38 99
39 PasswordSyncableService::PasswordSyncableService( 100 PasswordSyncableService::PasswordSyncableService(
40 scoped_refptr<PasswordStore> password_store) 101 scoped_refptr<PasswordStore> password_store)
41 : password_store_(password_store) { 102 : password_store_(password_store) {
42 } 103 }
43 104
44 PasswordSyncableService::~PasswordSyncableService() {} 105 PasswordSyncableService::~PasswordSyncableService() {}
45 106
46 syncer::SyncMergeResult 107 syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
47 PasswordSyncableService::MergeDataAndStartSyncing(
48 syncer::ModelType type, 108 syncer::ModelType type,
49 const syncer::SyncDataList& initial_sync_data, 109 const syncer::SyncDataList& initial_sync_data,
50 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 110 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
51 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { 111 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
112 DCHECK_EQ(syncer::PASSWORDS, type);
52 syncer::SyncMergeResult merge_result(type); 113 syncer::SyncMergeResult merge_result(type);
53 sync_error_factory_ = sync_error_factory.Pass(); 114 sync_error_factory_ = sync_error_factory.Pass();
54 sync_processor_ = sync_processor.Pass(); 115 sync_processor_ = sync_processor.Pass();
55 116
56 merge_result.set_error(sync_error_factory->CreateAndUploadError( 117 ScopedVector<autofill::PasswordForm> password_entries;
57 FROM_HERE, 118 if (!password_store_->FillAutofillableLogins(&password_entries.get())) {
58 "Password Syncable Service Not Implemented.")); 119 // Password store often fails to load passwords. Track failures with UMA.
120 // (http://crbug.com/249000)
121 UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
122 syncer::PASSWORDS,
123 syncer::MODEL_TYPE_COUNT);
124 merge_result.set_error(sync_error_factory_->CreateAndUploadError(
125 FROM_HERE,
126 "Failed to get passwords from store."));
127 return merge_result;
128 }
129
130 PasswordEntryMap new_local_entries;
131 for (PasswordForms::iterator it = password_entries.begin();
132 it != password_entries.end(); ++it) {
133 autofill::PasswordForm* password_form = *it;
134 // We add all the db entries as |new_local_entries| initially. During
135 // model association entries that match a sync entry will be
136 // removed and this list will only contain entries that are not in sync.
137 new_local_entries.insert(
138 std::make_pair(MakePasswordSyncTag(*password_form), password_form));
139 }
140
141 merge_result.set_num_items_before_association(new_local_entries.size());
142
143 // List that contains the entries that are known only to sync.
144 ScopedVector<autofill::PasswordForm> new_sync_entries;
145
146 // List that contains the entries that are known to both sync and db but
147 // have updates in sync. They need to be updated in the passwords db.
148 ScopedVector<autofill::PasswordForm> updated_sync_entries;
149
150 // Changes from password db that need to be propagated to sync.
151 syncer::SyncChangeList updated_db_entries;
152 for (syncer::SyncDataList::const_iterator sync_iter =
153 initial_sync_data.begin();
154 sync_iter != initial_sync_data.end(); ++sync_iter) {
155 CreateOrUpdateEntry(*sync_iter,
156 &new_local_entries,
157 &new_sync_entries,
158 &updated_sync_entries,
159 &updated_db_entries);
160 }
161
162 WriteToPasswordStore(new_sync_entries.get(),
163 updated_sync_entries.get());
164
165 merge_result.set_num_items_after_association(
166 merge_result.num_items_before_association() + new_sync_entries.size());
167
168 merge_result.set_num_items_added(new_sync_entries.size());
169
170 merge_result.set_num_items_modified(updated_sync_entries.size());
171
172 for (PasswordEntryMap::iterator it = new_local_entries.begin();
173 it != new_local_entries.end();
174 ++it) {
175 updated_db_entries.push_back(
176 syncer::SyncChange(FROM_HERE,
177 syncer::SyncChange::ACTION_ADD,
178 SyncDataFromPassword(*it->second)));
179 }
180
181 merge_result.set_error(
182 sync_processor_->ProcessSyncChanges(FROM_HERE, updated_db_entries));
59 return merge_result; 183 return merge_result;
60 } 184 }
61 185
62 void PasswordSyncableService::StopSyncing(syncer::ModelType type) { 186 void PasswordSyncableService::StopSyncing(syncer::ModelType type) {
63 sync_processor_.reset(); 187 sync_processor_.reset();
64 sync_error_factory_.reset(); 188 sync_error_factory_.reset();
65 } 189 }
66 190
67 syncer::SyncDataList PasswordSyncableService::GetAllSyncData( 191 syncer::SyncDataList PasswordSyncableService::GetAllSyncData(
68 syncer::ModelType type) const { 192 syncer::ModelType type) const {
(...skipping 12 matching lines...) Expand all
81 } 205 }
82 206
83 void PasswordSyncableService::ActOnPasswordStoreChanges( 207 void PasswordSyncableService::ActOnPasswordStoreChanges(
84 const PasswordStoreChangeList& local_changes) { 208 const PasswordStoreChangeList& local_changes) {
85 if (!sync_processor_) 209 if (!sync_processor_)
86 return; 210 return;
87 syncer::SyncChangeList sync_changes; 211 syncer::SyncChangeList sync_changes;
88 for (PasswordStoreChangeList::const_iterator it = local_changes.begin(); 212 for (PasswordStoreChangeList::const_iterator it = local_changes.begin();
89 it != local_changes.end(); 213 it != local_changes.end();
90 ++it) { 214 ++it) {
91 sync_changes.push_back(syncer::SyncChange(FROM_HERE, 215 sync_changes.push_back(
92 GetSyncChangeType(it->type()), 216 syncer::SyncChange(FROM_HERE,
93 CreateSyncData(it->form()))); 217 GetSyncChangeType(it->type()),
218 SyncDataFromPassword(it->form())));
94 } 219 }
95 sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes); 220 sync_processor_->ProcessSyncChanges(FROM_HERE, sync_changes);
96 } 221 }
97 222
98 // static
99 std::string PasswordSyncableService::MakeTag(
100 const std::string& origin_url,
101 const std::string& username_element,
102 const std::string& username_value,
103 const std::string& password_element,
104 const std::string& signon_realm) {
105 return net::EscapePath(origin_url) + "|" +
106 net::EscapePath(username_element) + "|" +
107 net::EscapePath(username_value) + "|" +
108 net::EscapePath(password_element) + "|" +
109 net::EscapePath(signon_realm);
110 }
111
112 // static
113 std::string PasswordSyncableService::MakeTag(
114 const autofill::PasswordForm& password) {
115 return MakeTag(password.origin.spec(),
116 base::UTF16ToUTF8(password.username_element),
117 base::UTF16ToUTF8(password.username_value),
118 base::UTF16ToUTF8(password.password_element),
119 password.signon_realm);
120 }
121
122 // static
123 std::string PasswordSyncableService::MakeTag(
124 const sync_pb::PasswordSpecificsData& password) {
125 return MakeTag(password.origin(),
126 password.username_element(),
127 password.username_value(),
128 password.password_element(),
129 password.signon_realm());
130 }
131
132 void PasswordSyncableService::WriteToPasswordStore( 223 void PasswordSyncableService::WriteToPasswordStore(
133 PasswordForms* new_entries, 224 const PasswordForms& new_entries,
134 PasswordForms* updated_entries) { 225 const PasswordForms& updated_entries) {
135 PasswordStoreChangeList changes; 226 PasswordStoreChangeList changes;
136 for (std::vector<autofill::PasswordForm*>::const_iterator it = 227 for (std::vector<autofill::PasswordForm*>::const_iterator it =
137 new_entries->begin(); 228 new_entries.begin();
138 it != new_entries->end(); 229 it != new_entries.end();
139 ++it) { 230 ++it) {
140 AppendChanges(password_store_->AddLoginImpl(**it), &changes); 231 AppendChanges(password_store_->AddLoginImpl(**it), &changes);
141 } 232 }
142 233
143 for (std::vector<autofill::PasswordForm*>::const_iterator it = 234 for (std::vector<autofill::PasswordForm*>::const_iterator it =
144 updated_entries->begin(); 235 updated_entries.begin();
145 it != updated_entries->end(); 236 it != updated_entries.end();
146 ++it) { 237 ++it) {
147 AppendChanges(password_store_->UpdateLoginImpl(**it), &changes); 238 AppendChanges(password_store_->UpdateLoginImpl(**it), &changes);
148 } 239 }
149 240
150 // We have to notify password store observers of the change by hand since 241 // We have to notify password store observers of the change by hand since
151 // we use internal password store interfaces to make changes synchronously. 242 // we use internal password store interfaces to make changes synchronously.
243 NotifyPasswordStoreOfLoginChanges(changes);
244 }
245
246 void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
247 const PasswordStoreChangeList& changes) {
152 password_store_->NotifyLoginsChanged(changes); 248 password_store_->NotifyLoginsChanged(changes);
153 } 249 }
154 250
155 syncer::SyncData PasswordSyncableService::CreateSyncData( 251 void PasswordSyncableService::CreateOrUpdateEntry(
252 const syncer::SyncData& data,
253 PasswordEntryMap* umatched_data_from_password_db,
254 ScopedVector<autofill::PasswordForm>* new_sync_entries,
255 ScopedVector<autofill::PasswordForm>* updated_sync_entries,
256 syncer::SyncChangeList* updated_db_entries) {
257 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
258 const sync_pb::PasswordSpecificsData& password_specifics(
259 specifics.password().client_only_encrypted_data());
260 std::string tag = MakePasswordSyncTag(password_specifics);
261
262 // Check whether the data from sync is already in the password store.
263 PasswordEntryMap::iterator existing_local_entry_iter =
264 umatched_data_from_password_db->find(tag);
265 if (existing_local_entry_iter == umatched_data_from_password_db->end()) {
266 // The sync data is not in the password store, so we need to create it in
267 // the password store. Add the entry to the new_entries list.
268 scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
269 PasswordFromSpecifics(password_specifics, new_password.get());
270 new_sync_entries->push_back(new_password.release());
271 } else {
272 // The entry is in password store. If the entries are not identical, then
273 // the entries need to be merged.
274 scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm);
275 if (MergeLocalAndSyncPasswords(password_specifics,
276 *existing_local_entry_iter->second,
277 new_password.get())) {
278 // Rather than checking which database -- sync or local -- needs updating,
279 // simply push an update to both. This will end up being a noop for the
280 // database that didn't need an update.
281 updated_db_entries->push_back(
282 syncer::SyncChange(FROM_HERE,
283 syncer::SyncChange::ACTION_UPDATE,
284 SyncDataFromPassword(*new_password)));
285
286 updated_sync_entries->push_back(new_password.release());
287 }
288 // Remove the entry from the entry map to indicate a match has been found.
289 // Entries that remain in the map at the end of associating all sync entries
290 // will be treated as additions that need to be propagated to sync.
291 umatched_data_from_password_db->erase(existing_local_entry_iter);
292 }
293 }
294
295 syncer::SyncData SyncDataFromPassword(
156 const autofill::PasswordForm& password_form) { 296 const autofill::PasswordForm& password_form) {
157 sync_pb::EntitySpecifics password_data; 297 sync_pb::EntitySpecifics password_data;
158 sync_pb::PasswordSpecificsData* password_specifics = 298 sync_pb::PasswordSpecificsData* password_specifics =
159 password_data.mutable_password()->mutable_client_only_encrypted_data(); 299 password_data.mutable_password()->mutable_client_only_encrypted_data();
160 password_specifics->set_scheme(password_form.scheme); 300 password_specifics->set_scheme(password_form.scheme);
161 password_specifics->set_signon_realm(password_form.signon_realm); 301 password_specifics->set_signon_realm(password_form.signon_realm);
162 password_specifics->set_origin(password_form.origin.spec()); 302 password_specifics->set_origin(password_form.origin.spec());
163 password_specifics->set_action(password_form.action.spec()); 303 password_specifics->set_action(password_form.action.spec());
164 password_specifics->set_username_element( 304 password_specifics->set_username_element(
165 base::UTF16ToUTF8(password_form.username_element)); 305 base::UTF16ToUTF8(password_form.username_element));
166 password_specifics->set_password_element( 306 password_specifics->set_password_element(
167 base::UTF16ToUTF8(password_form.password_element)); 307 base::UTF16ToUTF8(password_form.password_element));
168 password_specifics->set_username_value( 308 password_specifics->set_username_value(
169 base::UTF16ToUTF8(password_form.username_value)); 309 base::UTF16ToUTF8(password_form.username_value));
170 password_specifics->set_password_value( 310 password_specifics->set_password_value(
171 base::UTF16ToUTF8(password_form.password_value)); 311 base::UTF16ToUTF8(password_form.password_value));
172 password_specifics->set_ssl_valid(password_form.ssl_valid); 312 password_specifics->set_ssl_valid(password_form.ssl_valid);
173 password_specifics->set_preferred(password_form.preferred); 313 password_specifics->set_preferred(password_form.preferred);
174 password_specifics->set_date_created( 314 password_specifics->set_date_created(
175 password_form.date_created.ToInternalValue()); 315 password_form.date_created.ToInternalValue());
176 password_specifics->set_blacklisted(password_form.blacklisted_by_user); 316 password_specifics->set_blacklisted(password_form.blacklisted_by_user);
177 317
178 std::string tag = MakeTag(*password_specifics); 318 std::string tag = MakePasswordSyncTag(*password_specifics);
179 return syncer::SyncData::CreateLocalData(tag, tag, password_data); 319 return syncer::SyncData::CreateLocalData(tag, tag, password_data);
180 } 320 }
321
322 void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password,
323 autofill::PasswordForm* new_password) {
324 new_password->scheme =
325 static_cast<autofill::PasswordForm::Scheme>(password.scheme());
326 new_password->signon_realm = password.signon_realm();
327 new_password->origin = GURL(password.origin());
328 new_password->action = GURL(password.action());
329 new_password->username_element =
330 base::UTF8ToUTF16(password.username_element());
331 new_password->password_element =
332 base::UTF8ToUTF16(password.password_element());
333 new_password->username_value = base::UTF8ToUTF16(password.username_value());
334 new_password->password_value = base::UTF8ToUTF16(password.password_value());
335 new_password->ssl_valid = password.ssl_valid();
336 new_password->preferred = password.preferred();
337 new_password->date_created =
338 base::Time::FromInternalValue(password.date_created());
339 new_password->blacklisted_by_user = password.blacklisted();
340 }
341
342 std::string MakePasswordSyncTag(
343 const sync_pb::PasswordSpecificsData& password) {
344 return MakePasswordSyncTag(password.origin(),
345 password.username_element(),
346 password.username_value(),
347 password.password_element(),
348 password.signon_realm());
349 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698