Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / sync / glue / password_model_associator.cc
blobade8d6b2251367bbb1e46b3f3aeee945ddfdfc68
1 // Copyright (c) 2012 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/sync/glue/password_model_associator.h"
7 #include <set>
9 #include "base/location.h"
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/password_manager/password_store.h"
14 #include "chrome/browser/sync/profile_sync_service.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "net/base/escape.h"
17 #include "sync/api/sync_error.h"
18 #include "sync/internal_api/public/read_node.h"
19 #include "sync/internal_api/public/read_transaction.h"
20 #include "sync/internal_api/public/write_node.h"
21 #include "sync/internal_api/public/write_transaction.h"
22 #include "sync/protocol/password_specifics.pb.h"
24 using base::UTF8ToUTF16;
25 using base::UTF16ToUTF8;
26 using content::BrowserThread;
28 namespace browser_sync {
30 const char kPasswordTag[] = "google_chrome_passwords";
32 PasswordModelAssociator::PasswordModelAssociator(
33 ProfileSyncService* sync_service,
34 PasswordStore* password_store,
35 DataTypeErrorHandler* error_handler)
36 : sync_service_(sync_service),
37 password_store_(password_store),
38 password_node_id_(syncer::kInvalidId),
39 abort_association_requested_(false),
40 expected_loop_(base::MessageLoop::current()),
41 error_handler_(error_handler) {
42 DCHECK(sync_service_);
43 #if defined(OS_MACOSX)
44 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
45 #else
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
47 #endif
50 PasswordModelAssociator::~PasswordModelAssociator() {
51 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
54 syncer::SyncError PasswordModelAssociator::AssociateModels(
55 syncer::SyncMergeResult* local_merge_result,
56 syncer::SyncMergeResult* syncer_merge_result) {
57 DCHECK(expected_loop_ == base::MessageLoop::current());
59 PasswordVector new_passwords;
60 PasswordVector updated_passwords;
62 base::AutoLock lock(association_lock_);
63 if (abort_association_requested_)
64 return syncer::SyncError();
66 CHECK(password_store_.get());
68 // We must not be holding a transaction when we interact with the password
69 // store, as it can post tasks to the UI thread which can itself be blocked
70 // on our transaction, resulting in deadlock. (http://crbug.com/70658)
71 std::vector<autofill::PasswordForm*> passwords;
72 if (!password_store_->FillAutofillableLogins(&passwords) ||
73 !password_store_->FillBlacklistLogins(&passwords)) {
74 STLDeleteElements(&passwords);
76 // Password store often fails to load passwords. Track failures with UMA.
77 // (http://crbug.com/249000)
78 UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
79 ModelTypeToHistogramInt(syncer::PASSWORDS),
80 syncer::MODEL_TYPE_COUNT);
81 return syncer::SyncError(FROM_HERE,
82 syncer::SyncError::DATATYPE_ERROR,
83 "Could not get the password entries.",
84 model_type());
87 std::set<std::string> current_passwords;
88 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
89 syncer::ReadNode password_root(&trans);
90 if (password_root.InitByTagLookup(kPasswordTag) !=
91 syncer::BaseNode::INIT_OK) {
92 return error_handler_->CreateAndUploadError(
93 FROM_HERE,
94 "Server did not create the top-level password node. We "
95 "might be running against an out-of-date server.",
96 model_type());
99 for (std::vector<autofill::PasswordForm*>::iterator ix =
100 passwords.begin();
101 ix != passwords.end(); ++ix) {
102 std::string tag = MakeTag(**ix);
104 syncer::ReadNode node(&trans);
105 if (node.InitByClientTagLookup(syncer::PASSWORDS, tag) ==
106 syncer::BaseNode::INIT_OK) {
107 const sync_pb::PasswordSpecificsData& password =
108 node.GetPasswordSpecifics();
109 DCHECK_EQ(tag, MakeTag(password));
111 autofill::PasswordForm new_password;
113 if (MergePasswords(password, **ix, &new_password)) {
114 syncer::WriteNode write_node(&trans);
115 if (write_node.InitByClientTagLookup(syncer::PASSWORDS, tag) !=
116 syncer::BaseNode::INIT_OK) {
117 STLDeleteElements(&passwords);
118 return error_handler_->CreateAndUploadError(
119 FROM_HERE,
120 "Failed to edit password sync node.",
121 model_type());
123 WriteToSyncNode(new_password, &write_node);
124 updated_passwords.push_back(new_password);
127 Associate(&tag, node.GetId());
128 } else {
129 syncer::WriteNode node(&trans);
130 syncer::WriteNode::InitUniqueByCreationResult result =
131 node.InitUniqueByCreation(syncer::PASSWORDS, password_root, tag);
132 if (result != syncer::WriteNode::INIT_SUCCESS) {
133 STLDeleteElements(&passwords);
134 return error_handler_->CreateAndUploadError(
135 FROM_HERE,
136 "Failed to create password sync node.",
137 model_type());
140 WriteToSyncNode(**ix, &node);
142 Associate(&tag, node.GetId());
145 current_passwords.insert(tag);
148 STLDeleteElements(&passwords);
150 int64 sync_child_id = password_root.GetFirstChildId();
151 while (sync_child_id != syncer::kInvalidId) {
152 syncer::ReadNode sync_child_node(&trans);
153 if (sync_child_node.InitByIdLookup(sync_child_id) !=
154 syncer::BaseNode::INIT_OK) {
155 return error_handler_->CreateAndUploadError(
156 FROM_HERE,
157 "Failed to fetch child node.",
158 model_type());
160 const sync_pb::PasswordSpecificsData& password =
161 sync_child_node.GetPasswordSpecifics();
162 std::string tag = MakeTag(password);
164 // The password only exists on the server. Add it to the local
165 // model.
166 if (current_passwords.find(tag) == current_passwords.end()) {
167 autofill::PasswordForm new_password;
169 CopyPassword(password, &new_password);
170 Associate(&tag, sync_child_node.GetId());
171 new_passwords.push_back(new_password);
174 sync_child_id = sync_child_node.GetSuccessorId();
178 // We must not be holding a transaction when we interact with the password
179 // store, as it can post tasks to the UI thread which can itself be blocked
180 // on our transaction, resulting in deadlock. (http://crbug.com/70658)
181 return WriteToPasswordStore(&new_passwords,
182 &updated_passwords,
183 NULL);
186 bool PasswordModelAssociator::DeleteAllNodes(
187 syncer::WriteTransaction* trans) {
188 DCHECK(expected_loop_ == base::MessageLoop::current());
189 for (PasswordToSyncIdMap::iterator node_id = id_map_.begin();
190 node_id != id_map_.end(); ++node_id) {
191 syncer::WriteNode sync_node(trans);
192 if (sync_node.InitByIdLookup(node_id->second) !=
193 syncer::BaseNode::INIT_OK) {
194 LOG(ERROR) << "Typed url node lookup failed.";
195 return false;
197 sync_node.Tombstone();
200 id_map_.clear();
201 id_map_inverse_.clear();
202 return true;
205 syncer::SyncError PasswordModelAssociator::DisassociateModels() {
206 id_map_.clear();
207 id_map_inverse_.clear();
208 return syncer::SyncError();
211 bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
212 DCHECK(has_nodes);
213 *has_nodes = false;
214 int64 password_sync_id;
215 if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) {
216 LOG(ERROR) << "Server did not create the top-level password node. We "
217 << "might be running against an out-of-date server.";
218 return false;
220 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
222 syncer::ReadNode password_node(&trans);
223 if (password_node.InitByIdLookup(password_sync_id) !=
224 syncer::BaseNode::INIT_OK) {
225 LOG(ERROR) << "Server did not create the top-level password node. We "
226 << "might be running against an out-of-date server.";
227 return false;
230 // The sync model has user created nodes if the password folder has any
231 // children.
232 *has_nodes = password_node.HasChildren();
233 return true;
236 void PasswordModelAssociator::AbortAssociation() {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238 base::AutoLock lock(association_lock_);
239 abort_association_requested_ = true;
240 password_store_ = NULL;
243 bool PasswordModelAssociator::CryptoReadyIfNecessary() {
244 // We only access the cryptographer while holding a transaction.
245 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
246 // We always encrypt passwords, so no need to check if encryption is enabled.
247 return sync_service_->IsCryptographerReady(&trans);
250 const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId(
251 int64 sync_id) {
252 return NULL;
255 bool PasswordModelAssociator::InitSyncNodeFromChromeId(
256 const std::string& node_id,
257 syncer::BaseNode* sync_node) {
258 return false;
261 int64 PasswordModelAssociator::GetSyncIdFromChromeId(
262 const std::string& password) {
263 PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
264 return iter == id_map_.end() ? syncer::kInvalidId : iter->second;
267 void PasswordModelAssociator::Associate(
268 const std::string* password, int64 sync_id) {
269 DCHECK(expected_loop_ == base::MessageLoop::current());
270 DCHECK_NE(syncer::kInvalidId, sync_id);
271 DCHECK(id_map_.find(*password) == id_map_.end());
272 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
273 id_map_[*password] = sync_id;
274 id_map_inverse_[sync_id] = *password;
277 void PasswordModelAssociator::Disassociate(int64 sync_id) {
278 DCHECK(expected_loop_ == base::MessageLoop::current());
279 SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id);
280 if (iter == id_map_inverse_.end())
281 return;
282 CHECK(id_map_.erase(iter->second));
283 id_map_inverse_.erase(iter);
286 bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
287 int64* sync_id) {
288 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
289 syncer::ReadNode sync_node(&trans);
290 if (sync_node.InitByTagLookup(tag.c_str()) != syncer::BaseNode::INIT_OK)
291 return false;
292 *sync_id = sync_node.GetId();
293 return true;
296 syncer::SyncError PasswordModelAssociator::WriteToPasswordStore(
297 const PasswordVector* new_passwords,
298 const PasswordVector* updated_passwords,
299 const PasswordVector* deleted_passwords) {
300 base::AutoLock lock(association_lock_);
301 if (abort_association_requested_)
302 return syncer::SyncError();
304 CHECK(password_store_.get());
306 if (new_passwords) {
307 for (PasswordVector::const_iterator password = new_passwords->begin();
308 password != new_passwords->end(); ++password) {
309 password_store_->AddLoginImpl(*password);
313 if (updated_passwords) {
314 for (PasswordVector::const_iterator password = updated_passwords->begin();
315 password != updated_passwords->end(); ++password) {
316 password_store_->UpdateLoginImpl(*password);
320 if (deleted_passwords) {
321 for (PasswordVector::const_iterator password = deleted_passwords->begin();
322 password != deleted_passwords->end(); ++password) {
323 password_store_->RemoveLoginImpl(*password);
327 if (new_passwords || updated_passwords || deleted_passwords) {
328 // We have to notify password store observers of the change by hand since
329 // we use internal password store interfaces to make changes synchronously.
330 password_store_->PostNotifyLoginsChanged();
332 return syncer::SyncError();
335 // static
336 void PasswordModelAssociator::CopyPassword(
337 const sync_pb::PasswordSpecificsData& password,
338 autofill::PasswordForm* new_password) {
339 new_password->scheme =
340 static_cast<autofill::PasswordForm::Scheme>(password.scheme());
341 new_password->signon_realm = password.signon_realm();
342 new_password->origin = GURL(password.origin());
343 new_password->action = GURL(password.action());
344 new_password->username_element =
345 UTF8ToUTF16(password.username_element());
346 new_password->password_element =
347 UTF8ToUTF16(password.password_element());
348 new_password->username_value =
349 UTF8ToUTF16(password.username_value());
350 new_password->password_value =
351 UTF8ToUTF16(password.password_value());
352 new_password->ssl_valid = password.ssl_valid();
353 new_password->preferred = password.preferred();
354 new_password->date_created =
355 base::Time::FromInternalValue(password.date_created());
356 new_password->blacklisted_by_user =
357 password.blacklisted();
360 // static
361 bool PasswordModelAssociator::MergePasswords(
362 const sync_pb::PasswordSpecificsData& password,
363 const autofill::PasswordForm& password_form,
364 autofill::PasswordForm* new_password) {
365 DCHECK(new_password);
367 if (password.scheme() == password_form.scheme &&
368 password_form.signon_realm == password.signon_realm() &&
369 password_form.origin.spec() == password.origin() &&
370 password_form.action.spec() == password.action() &&
371 UTF16ToUTF8(password_form.username_element) ==
372 password.username_element() &&
373 UTF16ToUTF8(password_form.password_element) ==
374 password.password_element() &&
375 UTF16ToUTF8(password_form.username_value) ==
376 password.username_value() &&
377 UTF16ToUTF8(password_form.password_value) ==
378 password.password_value() &&
379 password.ssl_valid() == password_form.ssl_valid &&
380 password.preferred() == password_form.preferred &&
381 password.date_created() == password_form.date_created.ToInternalValue() &&
382 password.blacklisted() == password_form.blacklisted_by_user) {
383 return false;
386 // If the passwords differ, we take the one that was created more recently.
387 if (base::Time::FromInternalValue(password.date_created()) <=
388 password_form.date_created) {
389 *new_password = password_form;
390 } else {
391 CopyPassword(password, new_password);
394 return true;
397 // static
398 void PasswordModelAssociator::WriteToSyncNode(
399 const autofill::PasswordForm& password_form,
400 syncer::WriteNode* node) {
401 sync_pb::PasswordSpecificsData password;
402 password.set_scheme(password_form.scheme);
403 password.set_signon_realm(password_form.signon_realm);
404 password.set_origin(password_form.origin.spec());
405 password.set_action(password_form.action.spec());
406 password.set_username_element(UTF16ToUTF8(password_form.username_element));
407 password.set_password_element(UTF16ToUTF8(password_form.password_element));
408 password.set_username_value(UTF16ToUTF8(password_form.username_value));
409 password.set_password_value(UTF16ToUTF8(password_form.password_value));
410 password.set_ssl_valid(password_form.ssl_valid);
411 password.set_preferred(password_form.preferred);
412 password.set_date_created(password_form.date_created.ToInternalValue());
413 password.set_blacklisted(password_form.blacklisted_by_user);
415 node->SetPasswordSpecifics(password);
418 // static
419 std::string PasswordModelAssociator::MakeTag(
420 const autofill::PasswordForm& password) {
421 return MakeTag(password.origin.spec(),
422 UTF16ToUTF8(password.username_element),
423 UTF16ToUTF8(password.username_value),
424 UTF16ToUTF8(password.password_element),
425 password.signon_realm);
428 // static
429 std::string PasswordModelAssociator::MakeTag(
430 const sync_pb::PasswordSpecificsData& password) {
431 return MakeTag(password.origin(),
432 password.username_element(),
433 password.username_value(),
434 password.password_element(),
435 password.signon_realm());
438 // static
439 std::string PasswordModelAssociator::MakeTag(
440 const std::string& origin_url,
441 const std::string& username_element,
442 const std::string& username_value,
443 const std::string& password_element,
444 const std::string& signon_realm) {
445 return net::EscapePath(origin_url) + "|" +
446 net::EscapePath(username_element) + "|" +
447 net::EscapePath(username_value) + "|" +
448 net::EscapePath(password_element) + "|" +
449 net::EscapePath(signon_realm);
452 } // namespace browser_sync