OLD | NEW |
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 Loading... |
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 } |
OLD | NEW |