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

Side by Side Diff: components/password_manager/core/browser/password_syncable_service.cc

Issue 2981293003: Save Android Autofill credentials in the right format. (Closed)
Patch Set: New algorithm Created 3 years, 5 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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 "components/password_manager/core/browser/password_syncable_service.h" 5 #include "components/password_manager/core/browser/password_syncable_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <numeric>
9 #include <utility> 10 #include <utility>
10 11
11 #include "base/auto_reset.h" 12 #include "base/auto_reset.h"
12 #include "base/location.h" 13 #include "base/location.h"
13 #include "base/memory/ptr_util.h" 14 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
16 #include "base/optional.h"
17 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h"
19 #include "base/time/default_clock.h"
16 #include "components/autofill/core/common/password_form.h" 20 #include "components/autofill/core/common/password_form.h"
21 #include "components/password_manager/core/browser/android_affiliation/affiliati on_utils.h"
17 #include "components/password_manager/core/browser/password_manager_metrics_util .h" 22 #include "components/password_manager/core/browser/password_manager_metrics_util .h"
18 #include "components/password_manager/core/browser/password_store_sync.h" 23 #include "components/password_manager/core/browser/password_store_sync.h"
19 #include "components/sync/model/sync_error_factory.h" 24 #include "components/sync/model/sync_error_factory.h"
20 #include "net/base/escape.h" 25 #include "net/base/escape.h"
21 26
22 namespace password_manager { 27 namespace password_manager {
23 28
24 // Converts the |password| into a SyncData object. 29 // Converts the |password| into a SyncData object.
25 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password); 30 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password);
26 31
27 // Extracts the |PasswordForm| data from sync's protobuf format. 32 // Extracts the |PasswordForm| data from sync's protobuf format.
28 autofill::PasswordForm PasswordFromSpecifics( 33 autofill::PasswordForm PasswordFromSpecifics(
29 const sync_pb::PasswordSpecificsData& password); 34 const sync_pb::PasswordSpecificsData& password);
30 35
31 // Returns the unique tag that will serve as the sync identifier for the 36 // Returns the unique tag that will serve as the sync identifier for the
32 // |password| entry. 37 // |password| entry.
33 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password); 38 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password);
34 std::string MakePasswordSyncTag(const autofill::PasswordForm& password); 39 std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
35 40
36 namespace { 41 namespace {
37 42
38 // Returns true iff |password_specifics| and |password_specifics| are equal 43 // Returns true iff |password_specifics| and |password_specifics| are equal
39 // memberwise. 44 // in the fields which don't comprise the sync tag.
40 bool AreLocalAndSyncPasswordsEqual( 45 bool AreLocalAndSyncPasswordsNonSyncTagEqual(
41 const sync_pb::PasswordSpecificsData& password_specifics, 46 const sync_pb::PasswordSpecificsData& password_specifics,
42 const autofill::PasswordForm& password_form) { 47 const autofill::PasswordForm& password_form) {
43 return (password_form.scheme == password_specifics.scheme() && 48 return (password_form.scheme == password_specifics.scheme() &&
44 password_form.signon_realm == password_specifics.signon_realm() &&
45 password_form.origin.spec() == password_specifics.origin() &&
46 password_form.action.spec() == password_specifics.action() && 49 password_form.action.spec() == password_specifics.action() &&
47 base::UTF16ToUTF8(password_form.username_element) ==
48 password_specifics.username_element() &&
49 base::UTF16ToUTF8(password_form.password_element) ==
50 password_specifics.password_element() &&
51 base::UTF16ToUTF8(password_form.username_value) ==
52 password_specifics.username_value() &&
53 base::UTF16ToUTF8(password_form.password_value) == 50 base::UTF16ToUTF8(password_form.password_value) ==
54 password_specifics.password_value() && 51 password_specifics.password_value() &&
55 password_form.preferred == password_specifics.preferred() && 52 password_form.preferred == password_specifics.preferred() &&
56 password_form.date_created.ToInternalValue() == 53 password_form.date_created.ToInternalValue() ==
57 password_specifics.date_created() && 54 password_specifics.date_created() &&
58 password_form.blacklisted_by_user == 55 password_form.blacklisted_by_user ==
59 password_specifics.blacklisted() && 56 password_specifics.blacklisted() &&
60 password_form.type == password_specifics.type() && 57 password_form.type == password_specifics.type() &&
61 password_form.times_used == password_specifics.times_used() && 58 password_form.times_used == password_specifics.times_used() &&
62 base::UTF16ToUTF8(password_form.display_name) == 59 base::UTF16ToUTF8(password_form.display_name) ==
63 password_specifics.display_name() && 60 password_specifics.display_name() &&
64 password_form.icon_url.spec() == password_specifics.avatar_url() && 61 password_form.icon_url.spec() == password_specifics.avatar_url() &&
65 url::Origin(GURL(password_specifics.federation_url())).Serialize() == 62 url::Origin(GURL(password_specifics.federation_url())).Serialize() ==
66 password_form.federation_origin.Serialize()); 63 password_form.federation_origin.Serialize());
67 } 64 }
68 65
66 // Returns true iff |password_specifics| and |password_specifics| are equal
67 // memberwise.
68 bool AreLocalAndSyncPasswordsEqual(
69 const sync_pb::PasswordSpecificsData& password_specifics,
70 const autofill::PasswordForm& password_form) {
71 return (password_form.signon_realm == password_specifics.signon_realm() &&
72 password_form.origin.spec() == password_specifics.origin() &&
73 base::UTF16ToUTF8(password_form.username_element) ==
74 password_specifics.username_element() &&
75 base::UTF16ToUTF8(password_form.password_element) ==
76 password_specifics.password_element() &&
77 base::UTF16ToUTF8(password_form.username_value) ==
78 password_specifics.username_value() &&
79 AreLocalAndSyncPasswordsNonSyncTagEqual(password_specifics,
80 password_form));
81 }
82
83 // Compares the fields which are not part of the sync tag.
84 bool AreNonSyncTagFieldsEqual(const sync_pb::PasswordSpecificsData& left,
85 const sync_pb::PasswordSpecificsData& right) {
86 return (left.scheme() == right.scheme() && left.action() == right.action() &&
87 left.password_value() == right.password_value() &&
88 left.preferred() == right.preferred() &&
89 left.date_created() == right.date_created() &&
90 left.blacklisted() == right.blacklisted() &&
91 left.type() == right.type() &&
92 left.times_used() == right.times_used() &&
93 left.display_name() == right.display_name() &&
94 left.avatar_url() == right.avatar_url() &&
95 left.federation_url() == right.federation_url());
96 }
97
69 syncer::SyncChange::SyncChangeType GetSyncChangeType( 98 syncer::SyncChange::SyncChangeType GetSyncChangeType(
70 PasswordStoreChange::Type type) { 99 PasswordStoreChange::Type type) {
71 switch (type) { 100 switch (type) {
72 case PasswordStoreChange::ADD: 101 case PasswordStoreChange::ADD:
73 return syncer::SyncChange::ACTION_ADD; 102 return syncer::SyncChange::ACTION_ADD;
74 case PasswordStoreChange::UPDATE: 103 case PasswordStoreChange::UPDATE:
75 return syncer::SyncChange::ACTION_UPDATE; 104 return syncer::SyncChange::ACTION_UPDATE;
76 case PasswordStoreChange::REMOVE: 105 case PasswordStoreChange::REMOVE:
77 return syncer::SyncChange::ACTION_DELETE; 106 return syncer::SyncChange::ACTION_DELETE;
78 } 107 }
79 NOTREACHED(); 108 NOTREACHED();
80 return syncer::SyncChange::ACTION_INVALID; 109 return syncer::SyncChange::ACTION_INVALID;
81 } 110 }
82 111
83 // Creates a PasswordForm from |specifics| and |sync_time|, appends it to 112 // Creates a PasswordForm from |specifics| and |sync_time|, appends it to
84 // |entries|. 113 // |entries|.
85 void AppendPasswordFromSpecifics( 114 void AppendPasswordFromSpecifics(
86 const sync_pb::PasswordSpecificsData& specifics, 115 const sync_pb::PasswordSpecificsData& specifics,
87 base::Time sync_time, 116 base::Time sync_time,
88 std::vector<std::unique_ptr<autofill::PasswordForm>>* entries) { 117 std::vector<std::unique_ptr<autofill::PasswordForm>>* entries) {
89 entries->push_back(base::MakeUnique<autofill::PasswordForm>( 118 entries->push_back(base::MakeUnique<autofill::PasswordForm>(
90 PasswordFromSpecifics(specifics))); 119 PasswordFromSpecifics(specifics)));
91 entries->back()->date_synced = sync_time; 120 entries->back()->date_synced = sync_time;
92 } 121 }
93 122
123 // Android autofill saves credential in a different format without trailing '/'.
124 std::string GetAndroidAutofillSignonRealm(std::string android_autofill_realm) {
engedy 2017/07/20 18:34:14 For extra readability in the code below, how about
vasilii 2017/07/20 19:50:18 Done.
125 if (base::EndsWith(android_autofill_realm, "/", base::CompareCase::SENSITIVE))
126 android_autofill_realm.erase(android_autofill_realm.size() - 1);
127 return android_autofill_realm;
128 }
129
130 // The correct Android signon_realm should have a trailing '/'.
131 std::string GetCorrectAndroidSignonRealm(std::string android_realm) {
engedy 2017/07/20 18:34:14 Do we have complete confidence in [1] that we will
vasilii 2017/07/20 19:50:17 I didn't get the question. This particular functio
engedy 2017/07/20 20:12:14 Correct, no crashes, I was just wondering if havin
132 if (!base::EndsWith(android_realm, "/", base::CompareCase::SENSITIVE))
133 android_realm += '/';
134 return android_realm;
135 }
136
137 // Android autofill saves credential in a different format without trailing '/'.
engedy 2017/07/20 18:34:14 typo: credentials
vasilii 2017/07/20 19:50:18 Done.
138 // Return a sync tag for the style used by Android Autofill in the GMS Core v12.
engedy 2017/07/20 18:34:14 typo: s/in the GMS/in GMS/
vasilii 2017/07/20 19:50:18 Done.
139 std::string AndroidAutofillSyncTag(
140 const sync_pb::PasswordSpecificsData& password) {
141 // realm has the same value as the origin.
142 std::string origin = GetAndroidAutofillSignonRealm(password.origin());
engedy 2017/07/20 18:34:14 Just to triple check: -- Do you have some concre
vasilii 2017/07/20 19:50:17 - Yes, if you open the email thread then you can f
143 std::string signon_realm =
144 GetAndroidAutofillSignonRealm(password.signon_realm());
145 return (net::EscapePath(origin) + "|" +
146 net::EscapePath(password.username_element()) + "|" +
147 net::EscapePath(password.username_value()) + "|" +
148 net::EscapePath(password.password_element()) + "|" +
149 net::EscapePath(signon_realm));
150 }
151
152 // Return a sync tag for the correct format used by Android.
153 std::string AndroidCorrectSyncTag(
154 const sync_pb::PasswordSpecificsData& password) {
155 // realm has the same value as the origin.
156 std::string origin = GetCorrectAndroidSignonRealm(password.origin());
157 std::string signon_realm =
158 GetCorrectAndroidSignonRealm(password.signon_realm());
159 return (net::EscapePath(origin) + "|" +
160 net::EscapePath(password.username_element()) + "|" +
161 net::EscapePath(password.username_value()) + "|" +
162 net::EscapePath(password.password_element()) + "|" +
163 net::EscapePath(signon_realm));
164 }
165
166 void PasswordSpecificsFromPassword(
167 const autofill::PasswordForm& password_form,
168 sync_pb::PasswordSpecificsData* password_specifics) {
169 #define CopyField(field) password_specifics->set_##field(password_form.field)
170 #define CopyStringField(field) \
171 password_specifics->set_##field(base::UTF16ToUTF8(password_form.field))
172 CopyField(scheme);
173 CopyField(signon_realm);
174 password_specifics->set_origin(password_form.origin.spec());
175 password_specifics->set_action(password_form.action.spec());
176 CopyStringField(username_element);
177 CopyStringField(password_element);
178 CopyStringField(username_value);
179 CopyStringField(password_value);
180 CopyField(preferred);
181 password_specifics->set_date_created(
182 password_form.date_created.ToInternalValue());
183 password_specifics->set_blacklisted(password_form.blacklisted_by_user);
184 CopyField(type);
185 CopyField(times_used);
186 CopyStringField(display_name);
187 password_specifics->set_avatar_url(password_form.icon_url.spec());
188 password_specifics->set_federation_url(
189 password_form.federation_origin.unique()
190 ? std::string()
191 : password_form.federation_origin.Serialize());
192 #undef CopyStringField
193 #undef CopyField
194 }
195
196 struct AndroidMergeResult {
197 // New value for Android entry in the correct format.
198 base::Optional<syncer::SyncData> new_android_correct;
199 // New value for Android autofill entry.
200 base::Optional<syncer::SyncData> new_android_incorrect;
201 // New value for local entry in the correct format.
202 base::Optional<autofill::PasswordForm> new_local_correct;
203 // New value for local entry in the Android autofill format.
204 base::Optional<autofill::PasswordForm> new_local_incorrect;
205 };
206
207 // Perform deduplication of Android credentials saved in the wrong format. As
208 // the result all the four entries should be created and have the same value.
209 AndroidMergeResult Perform4WayMergeAndroidCredentials(
210 const sync_pb::PasswordSpecificsData* correct_android,
211 const sync_pb::PasswordSpecificsData* incorrect_android,
212 const autofill::PasswordForm* correct_local,
213 const autofill::PasswordForm* incorrect_local) {
214 AndroidMergeResult result;
215
216 base::Optional<sync_pb::PasswordSpecificsData> local_correct_ps;
217 if (correct_local) {
218 local_correct_ps = sync_pb::PasswordSpecificsData();
219 PasswordSpecificsFromPassword(*correct_local, &local_correct_ps.value());
220 }
221
222 base::Optional<sync_pb::PasswordSpecificsData> local_incorrect_ps;
223 if (incorrect_local) {
224 local_incorrect_ps = sync_pb::PasswordSpecificsData();
225 PasswordSpecificsFromPassword(*incorrect_local,
226 &local_incorrect_ps.value());
227 }
228
229 const sync_pb::PasswordSpecificsData* all_data[4] = {
230 correct_android, incorrect_android,
231 local_correct_ps ? &local_correct_ps.value() : nullptr,
232 local_incorrect_ps ? &local_incorrect_ps.value() : nullptr};
233
234 // |newest_data| will point to the newest entry out of all 4.
235 const sync_pb::PasswordSpecificsData* newest_data = nullptr;
236 newest_data =
engedy 2017/07/20 18:34:14 nit: Isn't this actually longer than a good old ra
vasilii 2017/07/20 19:50:17 Done.
237 std::accumulate(all_data, all_data + 4, newest_data,
238 [](const sync_pb::PasswordSpecificsData* newest,
239 const sync_pb::PasswordSpecificsData* current) {
240 if (newest && current) {
241 if (current->date_created() > newest->date_created())
242 newest = current;
243 } else if (current) {
244 newest = current;
245 }
246 return newest;
247 });
248 DCHECK(newest_data);
249
250 const std::string correct_tag = AndroidCorrectSyncTag(*newest_data);
engedy 2017/07/20 18:34:14 For brevity, have you considered precalculating th
vasilii 2017/07/20 19:50:18 Done.
251 const std::string incorrect_tag = AndroidAutofillSyncTag(*newest_data);
252 // Set the correct Sync entry if needed.
253 if (newest_data != correct_android &&
254 (!correct_android ||
255 !AreNonSyncTagFieldsEqual(*correct_android, *newest_data))) {
256 sync_pb::EntitySpecifics password_data;
257 sync_pb::PasswordSpecificsData* password_specifics =
258 password_data.mutable_password()->mutable_client_only_encrypted_data();
engedy 2017/07/20 18:34:14 To double check: are you familiar with how non-enc
vasilii 2017/07/20 19:50:18 Right, not our business.
259 *password_specifics = *newest_data;
260 password_specifics->set_origin(
261 GetCorrectAndroidSignonRealm(newest_data->origin()));
262 password_specifics->set_signon_realm(
263 GetCorrectAndroidSignonRealm(newest_data->signon_realm()));
264 result.new_android_correct = syncer::SyncData::CreateLocalData(
265 correct_tag, correct_tag, password_data);
266 }
267 // Set the Andoroid Autofill Sync entry if needed.
engedy 2017/07/20 18:34:14 optional nit: Consider blank lines between blocks.
vasilii 2017/07/20 19:50:18 Done.
268 if (newest_data != incorrect_android &&
269 (!incorrect_android ||
270 !AreNonSyncTagFieldsEqual(*incorrect_android, *newest_data))) {
271 sync_pb::EntitySpecifics password_data;
272 sync_pb::PasswordSpecificsData* password_specifics =
273 password_data.mutable_password()->mutable_client_only_encrypted_data();
274 *password_specifics = *newest_data;
275 password_specifics->set_origin(
276 GetAndroidAutofillSignonRealm(newest_data->origin()));
277 password_specifics->set_signon_realm(
278 GetAndroidAutofillSignonRealm(newest_data->signon_realm()));
279 result.new_android_incorrect = syncer::SyncData::CreateLocalData(
280 incorrect_tag, incorrect_tag, password_data);
281 }
282 // Set the correct local entry if needed.
283 if (!local_correct_ps ||
284 (newest_data != &local_correct_ps.value() &&
285 !AreNonSyncTagFieldsEqual(local_correct_ps.value(), *newest_data))) {
286 result.new_local_correct = PasswordFromSpecifics(*newest_data);
287 result.new_local_correct.value().origin =
288 GURL(GetCorrectAndroidSignonRealm(newest_data->origin()));
289 result.new_local_correct.value().signon_realm =
290 GetCorrectAndroidSignonRealm(newest_data->signon_realm());
291 }
292 // Set the incorrect local entry if needed.
293 if (!local_incorrect_ps ||
294 (newest_data != &local_incorrect_ps.value() &&
295 !AreNonSyncTagFieldsEqual(local_incorrect_ps.value(), *newest_data))) {
296 result.new_local_incorrect = PasswordFromSpecifics(*newest_data);
297 result.new_local_incorrect.value().origin =
298 GURL(GetAndroidAutofillSignonRealm(newest_data->origin()));
299 result.new_local_incorrect.value().signon_realm =
300 GetAndroidAutofillSignonRealm(newest_data->signon_realm());
301 }
302 return result;
303 }
304
94 } // namespace 305 } // namespace
95 306
96 struct PasswordSyncableService::SyncEntries { 307 struct PasswordSyncableService::SyncEntries {
97 std::vector<std::unique_ptr<autofill::PasswordForm>>* EntriesForChangeType( 308 std::vector<std::unique_ptr<autofill::PasswordForm>>* EntriesForChangeType(
98 syncer::SyncChange::SyncChangeType type) { 309 syncer::SyncChange::SyncChangeType type) {
99 switch (type) { 310 switch (type) {
100 case syncer::SyncChange::ACTION_ADD: 311 case syncer::SyncChange::ACTION_ADD:
101 return &new_entries; 312 return &new_entries;
102 case syncer::SyncChange::ACTION_UPDATE: 313 case syncer::SyncChange::ACTION_UPDATE:
103 return &updated_entries; 314 return &updated_entries;
(...skipping 13 matching lines...) Expand all
117 // database but have updates in sync. They need to be updated in the local 328 // database but have updates in sync. They need to be updated in the local
118 // database. 329 // database.
119 std::vector<std::unique_ptr<autofill::PasswordForm>> updated_entries; 330 std::vector<std::unique_ptr<autofill::PasswordForm>> updated_entries;
120 331
121 // The list of entries to be deleted from the local database. 332 // The list of entries to be deleted from the local database.
122 std::vector<std::unique_ptr<autofill::PasswordForm>> deleted_entries; 333 std::vector<std::unique_ptr<autofill::PasswordForm>> deleted_entries;
123 }; 334 };
124 335
125 PasswordSyncableService::PasswordSyncableService( 336 PasswordSyncableService::PasswordSyncableService(
126 PasswordStoreSync* password_store) 337 PasswordStoreSync* password_store)
127 : password_store_(password_store), is_processing_sync_changes_(false) { 338 : password_store_(password_store),
128 } 339 clock_(new base::DefaultClock),
340 is_processing_sync_changes_(false) {}
129 341
130 PasswordSyncableService::~PasswordSyncableService() { 342 PasswordSyncableService::~PasswordSyncableService() {
131 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 343 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
132 } 344 }
133 345
134 syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing( 346 syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
135 syncer::ModelType type, 347 syncer::ModelType type,
136 const syncer::SyncDataList& initial_sync_data, 348 const syncer::SyncDataList& initial_sync_data,
137 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, 349 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
138 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) { 350 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
(...skipping 17 matching lines...) Expand all
156 if (password_entries.size() != new_local_entries.size()) { 368 if (password_entries.size() != new_local_entries.size()) {
157 merge_result.set_error(sync_error_factory->CreateAndUploadError( 369 merge_result.set_error(sync_error_factory->CreateAndUploadError(
158 FROM_HERE, 370 FROM_HERE,
159 "There are passwords with identical sync tags in the database.")); 371 "There are passwords with identical sync tags in the database."));
160 metrics_util::LogPasswordSyncState( 372 metrics_util::LogPasswordSyncState(
161 metrics_util::NOT_SYNCING_DUPLICATE_TAGS); 373 metrics_util::NOT_SYNCING_DUPLICATE_TAGS);
162 return merge_result; 374 return merge_result;
163 } 375 }
164 merge_result.set_num_items_before_association(new_local_entries.size()); 376 merge_result.set_num_items_before_association(new_local_entries.size());
165 377
378 // Changes from Sync to be applied locally.
166 SyncEntries sync_entries; 379 SyncEntries sync_entries;
167 // Changes from password db that need to be propagated to sync. 380 // Changes from password db that need to be propagated to sync.
168 syncer::SyncChangeList updated_db_entries; 381 syncer::SyncChangeList updated_db_entries;
169 for (syncer::SyncDataList::const_iterator sync_iter = 382 MergeSyncDataWithLocalData(initial_sync_data, &new_local_entries,
170 initial_sync_data.begin(); 383 &sync_entries, &updated_db_entries);
171 sync_iter != initial_sync_data.end(); ++sync_iter) {
172 CreateOrUpdateEntry(*sync_iter,
173 &new_local_entries,
174 &sync_entries,
175 &updated_db_entries);
176 }
177 384
178 for (PasswordEntryMap::iterator it = new_local_entries.begin(); 385 for (PasswordEntryMap::iterator it = new_local_entries.begin();
179 it != new_local_entries.end(); ++it) { 386 it != new_local_entries.end(); ++it) {
180 updated_db_entries.push_back( 387 updated_db_entries.push_back(
181 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, 388 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD,
182 SyncDataFromPassword(*it->second))); 389 SyncDataFromPassword(*it->second)));
183 } 390 }
184 391
185 WriteToPasswordStore(sync_entries); 392 WriteToPasswordStore(sync_entries);
186 merge_result.set_error( 393 merge_result.set_error(
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 }); 437 });
231 return sync_data; 438 return sync_data;
232 } 439 }
233 440
234 syncer::SyncError PasswordSyncableService::ProcessSyncChanges( 441 syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
235 const tracked_objects::Location& from_here, 442 const tracked_objects::Location& from_here,
236 const syncer::SyncChangeList& change_list) { 443 const syncer::SyncChangeList& change_list) {
237 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 444 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
238 base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true); 445 base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
239 SyncEntries sync_entries; 446 SyncEntries sync_entries;
240 base::Time time_now = base::Time::Now(); 447 base::Time time_now = clock_->Now();
241 448
242 for (syncer::SyncChangeList::const_iterator it = change_list.begin(); 449 for (syncer::SyncChangeList::const_iterator it = change_list.begin();
243 it != change_list.end(); ++it) { 450 it != change_list.end(); ++it) {
244 const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics(); 451 const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics();
245 std::vector<std::unique_ptr<autofill::PasswordForm>>* entries = 452 std::vector<std::unique_ptr<autofill::PasswordForm>>* entries =
246 sync_entries.EntriesForChangeType(it->change_type()); 453 sync_entries.EntriesForChangeType(it->change_type());
247 if (!entries) { 454 if (!entries) {
248 return sync_error_factory_->CreateAndUploadError( 455 return sync_error_factory_->CreateAndUploadError(
249 FROM_HERE, "Failed to process sync changes for passwords datatype."); 456 FROM_HERE, "Failed to process sync changes for passwords datatype.");
250 } 457 }
251 AppendPasswordFromSpecifics( 458 AppendPasswordFromSpecifics(
252 specifics.password().client_only_encrypted_data(), time_now, entries); 459 specifics.password().client_only_encrypted_data(), time_now, entries);
460 if (IsValidAndroidFacetURI(entries->back()->signon_realm)) {
461 // Fix the Android Autofill credentials if needed.
462 entries->back()->signon_realm =
463 GetCorrectAndroidSignonRealm(entries->back()->signon_realm);
464 entries->back()->origin =
465 GURL(GetCorrectAndroidSignonRealm(entries->back()->origin.spec()));
466 }
253 } 467 }
254 468
255 WriteToPasswordStore(sync_entries); 469 WriteToPasswordStore(sync_entries);
256 return syncer::SyncError(); 470 return syncer::SyncError();
257 } 471 }
258 472
259 void PasswordSyncableService::ActOnPasswordStoreChanges( 473 void PasswordSyncableService::ActOnPasswordStoreChanges(
260 const PasswordStoreChangeList& local_changes) { 474 const PasswordStoreChangeList& local_changes) {
261 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 475 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
262 476
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 WriteEntriesToDatabase(&PasswordStoreSync::UpdateLoginSync, 546 WriteEntriesToDatabase(&PasswordStoreSync::UpdateLoginSync,
333 entries.updated_entries, &changes); 547 entries.updated_entries, &changes);
334 WriteEntriesToDatabase(&PasswordStoreSync::RemoveLoginSync, 548 WriteEntriesToDatabase(&PasswordStoreSync::RemoveLoginSync,
335 entries.deleted_entries, &changes); 549 entries.deleted_entries, &changes);
336 550
337 // We have to notify password store observers of the change by hand since 551 // We have to notify password store observers of the change by hand since
338 // we use internal password store interfaces to make changes synchronously. 552 // we use internal password store interfaces to make changes synchronously.
339 password_store_->NotifyLoginsChanged(changes); 553 password_store_->NotifyLoginsChanged(changes);
340 } 554 }
341 555
342 // static 556 void PasswordSyncableService::MergeSyncDataWithLocalData(
343 void PasswordSyncableService::CreateOrUpdateEntry( 557 const syncer::SyncDataList& sync_data,
344 const syncer::SyncData& data,
345 PasswordEntryMap* unmatched_data_from_password_db, 558 PasswordEntryMap* unmatched_data_from_password_db,
346 SyncEntries* sync_entries, 559 SyncEntries* sync_entries,
347 syncer::SyncChangeList* updated_db_entries) { 560 syncer::SyncChangeList* updated_db_entries) {
348 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); 561 std::map<std::string, const sync_pb::PasswordSpecificsData*> sync_data_map;
349 const sync_pb::PasswordSpecificsData& password_specifics( 562 for (const auto& data : sync_data) {
350 specifics.password().client_only_encrypted_data()); 563 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
564 const sync_pb::PasswordSpecificsData* password_specifics =
565 &specifics.password().client_only_encrypted_data();
566 sync_data_map[MakePasswordSyncTag(*password_specifics)] =
engedy 2017/07/20 18:34:14 Should we add a DCHECK() that there was no such ke
vasilii 2017/07/20 19:50:18 Done.
567 password_specifics;
568 }
569
570 for (auto it = sync_data_map.begin(); it != sync_data_map.end();) {
571 if (IsValidAndroidFacetURI(it->second->signon_realm())) {
572 // Perform deduplication of Android credentials saved in the wrong format.
573 // An incorrect entry is copied in the correct format so Chrome can make
engedy 2017/07/20 18:34:14 phrasing nit: For each incorrect entry, a duplicat
vasilii 2017/07/20 19:50:17 Done.
574 // use of it. The wrong sync entries are not deleted for now.
engedy 2017/07/20 18:34:14 phrasing nit: s/wrong/incorrect/
vasilii 2017/07/20 19:50:18 Done.
575 std::string incorrect_tag = AndroidAutofillSyncTag(*it->second);
576 std::string correct_tag = AndroidCorrectSyncTag(*it->second);
577 auto it_android_incorrect = sync_data_map.find(incorrect_tag);
engedy 2017/07/20 18:34:14 For extra readability, have you considered s/andro
vasilii 2017/07/20 19:50:17 Done.
578 auto it_android_correct = sync_data_map.find(correct_tag);
579 auto it_local_data_correct =
580 unmatched_data_from_password_db->find(correct_tag);
581 auto it_local_data_incorrect =
582 unmatched_data_from_password_db->find(incorrect_tag);
engedy 2017/07/20 18:34:14 Should we fall back to default handling if (it !=
vasilii 2017/07/20 19:50:18 Good point. They claim that they use the same lib
583 if (it_android_incorrect == sync_data_map.end() &&
584 it_local_data_incorrect == unmatched_data_from_password_db->end()) {
585 // Wrong credential don't exist. Just do what Sync would normally do.
586 CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db,
587 sync_entries, updated_db_entries);
588 ++it;
589 } else {
590 AndroidMergeResult result = Perform4WayMergeAndroidCredentials(
591 it_android_correct == sync_data_map.end()
592 ? nullptr
593 : it_android_correct->second,
594 it_android_incorrect == sync_data_map.end()
595 ? nullptr
596 : it_android_incorrect->second,
597 it_local_data_correct == unmatched_data_from_password_db->end()
598 ? nullptr
599 : it_local_data_correct->second,
600 it_local_data_incorrect == unmatched_data_from_password_db->end()
601 ? nullptr
602 : it_local_data_incorrect->second);
603 // Add or update the correct local entry.
604 if (result.new_local_correct) {
605 auto* local_changes = sync_entries->EntriesForChangeType(
606 it_local_data_correct == unmatched_data_from_password_db->end()
607 ? syncer::SyncChange::ACTION_ADD
608 : syncer::SyncChange::ACTION_UPDATE);
609 local_changes->push_back(base::MakeUnique<autofill::PasswordForm>(
610 result.new_local_correct.value()));
611 local_changes->back()->date_synced = clock_->Now();
612 }
613 // Add or update the incorrect local entry.
614 if (result.new_local_incorrect) {
615 auto* local_changes = sync_entries->EntriesForChangeType(
616 it_local_data_incorrect == unmatched_data_from_password_db->end()
617 ? syncer::SyncChange::ACTION_ADD
618 : syncer::SyncChange::ACTION_UPDATE);
619 local_changes->push_back(base::MakeUnique<autofill::PasswordForm>(
620 result.new_local_incorrect.value()));
621 local_changes->back()->date_synced = clock_->Now();
622 }
623 if (it_local_data_correct != unmatched_data_from_password_db->end())
624 unmatched_data_from_password_db->erase(it_local_data_correct);
625 if (it_local_data_incorrect != unmatched_data_from_password_db->end())
626 unmatched_data_from_password_db->erase(it_local_data_incorrect);
627 // Add or update the correct sync entry.
628 if (result.new_android_correct) {
629 updated_db_entries->push_back(
630 syncer::SyncChange(FROM_HERE,
631 it_android_correct == sync_data_map.end()
632 ? syncer::SyncChange::ACTION_ADD
633 : syncer::SyncChange::ACTION_UPDATE,
634 result.new_android_correct.value()));
635 }
636 // Add or update the Android Autofill sync entry.
637 if (result.new_android_incorrect) {
638 updated_db_entries->push_back(
639 syncer::SyncChange(FROM_HERE,
640 it_android_incorrect == sync_data_map.end()
641 ? syncer::SyncChange::ACTION_ADD
642 : syncer::SyncChange::ACTION_UPDATE,
643 result.new_android_incorrect.value()));
644 }
645 bool increment = true;
646 for (auto sync_data_it : {it_android_incorrect, it_android_correct}) {
647 if (sync_data_it != sync_data_map.end()) {
648 if (sync_data_it == it) {
649 it = sync_data_map.erase(it);
650 increment = false;
651 } else {
652 sync_data_map.erase(sync_data_it);
653 }
654 }
655 }
656 if (increment)
657 ++it;
658 }
659 } else {
660 // Not Android.
661 CreateOrUpdateEntry(*it->second, unmatched_data_from_password_db,
662 sync_entries, updated_db_entries);
663 ++it;
664 }
665 }
666 }
667
668 void PasswordSyncableService::CreateOrUpdateEntry(
669 const sync_pb::PasswordSpecificsData& password_specifics,
670 PasswordEntryMap* unmatched_data_from_password_db,
671 SyncEntries* sync_entries,
672 syncer::SyncChangeList* updated_db_entries) {
351 std::string tag = MakePasswordSyncTag(password_specifics); 673 std::string tag = MakePasswordSyncTag(password_specifics);
352 674
353 // Check whether the data from sync is already in the password store. 675 // Check whether the data from sync is already in the password store.
354 PasswordEntryMap::iterator existing_local_entry_iter = 676 PasswordEntryMap::iterator existing_local_entry_iter =
355 unmatched_data_from_password_db->find(tag); 677 unmatched_data_from_password_db->find(tag);
356 base::Time time_now = base::Time::Now(); 678 base::Time time_now = clock_->Now();
357 if (existing_local_entry_iter == unmatched_data_from_password_db->end()) { 679 if (existing_local_entry_iter == unmatched_data_from_password_db->end()) {
358 // The sync data is not in the password store, so we need to create it in 680 // The sync data is not in the password store, so we need to create it in
359 // the password store. Add the entry to the new_entries list. 681 // the password store. Add the entry to the new_entries list.
360 AppendPasswordFromSpecifics(password_specifics, time_now, 682 AppendPasswordFromSpecifics(password_specifics, time_now,
361 &sync_entries->new_entries); 683 &sync_entries->new_entries);
362 } else { 684 } else {
363 // The entry is in password store. If the entries are not identical, then 685 // The entry is in password store. If the entries are not identical, then
364 // the entries need to be merged. 686 // the entries need to be merged.
365 // If the passwords differ, take the one that was created more recently. 687 // If the passwords differ, take the one that was created more recently.
366 const autofill::PasswordForm& password_form = 688 const autofill::PasswordForm& password_form =
(...skipping 27 matching lines...) Expand all
394 new_changes.begin(), 716 new_changes.begin(),
395 new_changes.end()); 717 new_changes.end());
396 } 718 }
397 } 719 }
398 720
399 syncer::SyncData SyncDataFromPassword( 721 syncer::SyncData SyncDataFromPassword(
400 const autofill::PasswordForm& password_form) { 722 const autofill::PasswordForm& password_form) {
401 sync_pb::EntitySpecifics password_data; 723 sync_pb::EntitySpecifics password_data;
402 sync_pb::PasswordSpecificsData* password_specifics = 724 sync_pb::PasswordSpecificsData* password_specifics =
403 password_data.mutable_password()->mutable_client_only_encrypted_data(); 725 password_data.mutable_password()->mutable_client_only_encrypted_data();
404 #define CopyField(field) password_specifics->set_##field(password_form.field) 726 PasswordSpecificsFromPassword(password_form, password_specifics);
405 #define CopyStringField(field) \
406 password_specifics->set_##field(base::UTF16ToUTF8(password_form.field))
407 CopyField(scheme);
408 CopyField(signon_realm);
409 password_specifics->set_origin(password_form.origin.spec());
410 password_specifics->set_action(password_form.action.spec());
411 CopyStringField(username_element);
412 CopyStringField(password_element);
413 CopyStringField(username_value);
414 CopyStringField(password_value);
415 CopyField(preferred);
416 password_specifics->set_date_created(
417 password_form.date_created.ToInternalValue());
418 password_specifics->set_blacklisted(password_form.blacklisted_by_user);
419 CopyField(type);
420 CopyField(times_used);
421 CopyStringField(display_name);
422 password_specifics->set_avatar_url(password_form.icon_url.spec());
423 password_specifics->set_federation_url(
424 password_form.federation_origin.unique()
425 ? std::string()
426 : password_form.federation_origin.Serialize());
427 #undef CopyStringField
428 #undef CopyField
429 727
430 std::string tag = MakePasswordSyncTag(*password_specifics); 728 std::string tag = MakePasswordSyncTag(*password_specifics);
431 return syncer::SyncData::CreateLocalData(tag, tag, password_data); 729 return syncer::SyncData::CreateLocalData(tag, tag, password_data);
432 } 730 }
433 731
434 autofill::PasswordForm PasswordFromSpecifics( 732 autofill::PasswordForm PasswordFromSpecifics(
435 const sync_pb::PasswordSpecificsData& password) { 733 const sync_pb::PasswordSpecificsData& password) {
436 autofill::PasswordForm new_password; 734 autofill::PasswordForm new_password;
437 new_password.scheme = 735 new_password.scheme =
438 static_cast<autofill::PasswordForm::Scheme>(password.scheme()); 736 static_cast<autofill::PasswordForm::Scheme>(password.scheme());
(...skipping 26 matching lines...) Expand all
465 763
466 std::string MakePasswordSyncTag(const autofill::PasswordForm& password) { 764 std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
467 return (net::EscapePath(password.origin.spec()) + "|" + 765 return (net::EscapePath(password.origin.spec()) + "|" +
468 net::EscapePath(base::UTF16ToUTF8(password.username_element)) + "|" + 766 net::EscapePath(base::UTF16ToUTF8(password.username_element)) + "|" +
469 net::EscapePath(base::UTF16ToUTF8(password.username_value)) + "|" + 767 net::EscapePath(base::UTF16ToUTF8(password.username_value)) + "|" +
470 net::EscapePath(base::UTF16ToUTF8(password.password_element)) + "|" + 768 net::EscapePath(base::UTF16ToUTF8(password.password_element)) + "|" +
471 net::EscapePath(password.signon_realm)); 769 net::EscapePath(password.signon_realm));
472 } 770 }
473 771
474 } // namespace password_manager 772 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698