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_change_processor.h"
9 #include "base/location.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/sync/glue/password_model_associator.h"
14 #include "chrome/browser/sync/profile_sync_service.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/password_manager/core/browser/password_store.h"
17 #include "components/password_manager/core/browser/password_store_change.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "sync/internal_api/public/change_record.h"
20 #include "sync/internal_api/public/read_node.h"
21 #include "sync/internal_api/public/write_node.h"
22 #include "sync/internal_api/public/write_transaction.h"
23 #include "sync/protocol/password_specifics.pb.h"
25 using content::BrowserThread;
27 namespace browser_sync {
29 PasswordChangeProcessor::PasswordChangeProcessor(
30 PasswordModelAssociator* model_associator,
31 PasswordStore* password_store,
32 DataTypeErrorHandler* error_handler)
33 : ChangeProcessor(error_handler),
34 model_associator_(model_associator),
35 password_store_(password_store),
36 expected_loop_(base::MessageLoop::current()),
37 disconnected_(false) {
38 DCHECK(model_associator);
39 DCHECK(error_handler);
40 DCHECK(password_store->GetBackgroundTaskRunner()->RunsTasksOnCurrentThread());
43 PasswordChangeProcessor::~PasswordChangeProcessor() {
44 base::AutoLock lock(disconnect_lock_);
45 password_store_->RemoveObserver(this);
48 void PasswordChangeProcessor::OnLoginsChanged(
49 const PasswordStoreChangeList& changes) {
50 DCHECK(expected_loop_ == base::MessageLoop::current());
52 base::AutoLock lock(disconnect_lock_);
56 syncer::WriteTransaction trans(FROM_HERE, share_handle());
58 syncer::ReadNode password_root(&trans);
59 if (password_root.InitByTagLookup(kPasswordTag) !=
60 syncer::BaseNode::INIT_OK) {
61 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
62 "Server did not create the top-level password node. "
63 "We might be running against an out-of-date server.");
67 for (PasswordStoreChangeList::const_iterator change = changes.begin();
68 change != changes.end(); ++change) {
69 std::string tag = PasswordModelAssociator::MakeTag(change->form());
70 switch (change->type()) {
71 case PasswordStoreChange::ADD: {
72 syncer::WriteNode sync_node(&trans);
73 syncer::WriteNode::InitUniqueByCreationResult result =
74 sync_node.InitUniqueByCreation(syncer::PASSWORDS, password_root,
76 if (result == syncer::WriteNode::INIT_SUCCESS) {
77 PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
78 model_associator_->Associate(&tag, sync_node.GetId());
81 // Maybe this node already exists and we should update it.
83 // If the PasswordStore is told to add an entry but an entry with the
84 // same name already exists, it will overwrite it. It will report
85 // this change as an ADD rather than an UPDATE. Ideally, it would be
86 // able to tell us what action was actually taken, rather than what
87 // action was requested. If it did so, we wouldn't need to fall back
88 // to trying to update an existing password node here.
90 // TODO: Remove this. See crbug.com/87855.
91 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
92 if (syncer::kInvalidId == sync_id) {
93 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
94 "Unable to create or retrieve password node");
95 LOG(ERROR) << "Invalid sync id.";
98 if (sync_node.InitByIdLookup(sync_id) !=
99 syncer::BaseNode::INIT_OK) {
100 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
101 "Password node lookup failed.");
102 LOG(ERROR) << "Password node lookup failed.";
105 PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
109 case PasswordStoreChange::UPDATE: {
110 syncer::WriteNode sync_node(&trans);
111 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
112 if (syncer::kInvalidId == sync_id) {
113 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
115 LOG(ERROR) << "Invalid sync id.";
118 if (sync_node.InitByIdLookup(sync_id) !=
119 syncer::BaseNode::INIT_OK) {
120 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
121 "Password node lookup failed.");
122 LOG(ERROR) << "Password node lookup failed.";
127 PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
130 case PasswordStoreChange::REMOVE: {
131 syncer::WriteNode sync_node(&trans);
132 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
133 if (syncer::kInvalidId == sync_id) {
134 // We've been asked to remove a password that we don't know about.
135 // That's weird, but apparently we were already in the requested
136 // state, so it's not really an unrecoverable error. Just return.
137 LOG(WARNING) << "Trying to delete nonexistent password sync node!";
140 if (sync_node.InitByIdLookup(sync_id) !=
141 syncer::BaseNode::INIT_OK) {
142 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
143 "Password node lookup failed.");
146 model_associator_->Disassociate(sync_node.GetId());
147 sync_node.Tombstone();
155 void PasswordChangeProcessor::ApplyChangesFromSyncModel(
156 const syncer::BaseTransaction* trans,
158 const syncer::ImmutableChangeRecordList& changes) {
159 DCHECK(expected_loop_ == base::MessageLoop::current());
160 base::AutoLock lock(disconnect_lock_);
164 syncer::ReadNode password_root(trans);
165 if (password_root.InitByTagLookup(kPasswordTag) !=
166 syncer::BaseNode::INIT_OK) {
167 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
168 "Password root node lookup failed.");
172 DCHECK(deleted_passwords_.empty() && new_passwords_.empty() &&
173 updated_passwords_.empty());
175 for (syncer::ChangeRecordList::const_iterator it =
176 changes.Get().begin(); it != changes.Get().end(); ++it) {
177 if (syncer::ChangeRecord::ACTION_DELETE ==
179 DCHECK(it->specifics.has_password())
180 << "Password specifics data not present on delete!";
181 DCHECK(it->extra.get());
182 syncer::ExtraPasswordChangeRecordData* extra =
184 const sync_pb::PasswordSpecificsData& password = extra->unencrypted();
185 autofill::PasswordForm form;
186 PasswordModelAssociator::CopyPassword(password, &form);
187 deleted_passwords_.push_back(form);
188 model_associator_->Disassociate(it->id);
192 syncer::ReadNode sync_node(trans);
193 if (sync_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
194 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
195 "Password node lookup failed.");
199 // Check that the changed node is a child of the passwords folder.
200 DCHECK_EQ(password_root.GetId(), sync_node.GetParentId());
201 DCHECK_EQ(syncer::PASSWORDS, sync_node.GetModelType());
203 const sync_pb::PasswordSpecificsData& password_data =
204 sync_node.GetPasswordSpecifics();
205 autofill::PasswordForm password;
206 PasswordModelAssociator::CopyPassword(password_data, &password);
208 if (syncer::ChangeRecord::ACTION_ADD == it->action) {
209 std::string tag(PasswordModelAssociator::MakeTag(password));
210 model_associator_->Associate(&tag, sync_node.GetId());
211 new_passwords_.push_back(password);
213 DCHECK_EQ(syncer::ChangeRecord::ACTION_UPDATE, it->action);
214 updated_passwords_.push_back(password);
219 void PasswordChangeProcessor::CommitChangesFromSyncModel() {
220 DCHECK(expected_loop_ == base::MessageLoop::current());
221 base::AutoLock lock(disconnect_lock_);
225 ScopedStopObserving<PasswordChangeProcessor> stop_observing(this);
227 syncer::SyncError error = model_associator_->WriteToPasswordStore(
230 &deleted_passwords_);
232 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
233 "Error writing passwords");
237 deleted_passwords_.clear();
238 new_passwords_.clear();
239 updated_passwords_.clear();
242 void PasswordChangeProcessor::Disconnect() {
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244 base::AutoLock lock(disconnect_lock_);
245 disconnected_ = true;
248 void PasswordChangeProcessor::StartImpl(Profile* profile) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250 password_store_->ScheduleTask(
251 base::Bind(&PasswordChangeProcessor::InitObserving,
252 base::Unretained(this)));
255 void PasswordChangeProcessor::InitObserving() {
256 base::AutoLock lock(disconnect_lock_);
262 void PasswordChangeProcessor::StartObserving() {
263 DCHECK(expected_loop_ == base::MessageLoop::current());
264 disconnect_lock_.AssertAcquired();
265 password_store_->AddObserver(this);
268 void PasswordChangeProcessor::StopObserving() {
269 DCHECK(expected_loop_ == base::MessageLoop::current());
270 disconnect_lock_.AssertAcquired();
271 password_store_->RemoveObserver(this);
274 } // namespace browser_sync