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

Side by Side Diff: chrome/browser/sync/glue/password_model_associator.cc

Issue 1851004: Adding sync support for Passwords (Closed)
Patch Set: Ready for checkin Created 10 years, 7 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
(Empty)
1 // Copyright (c) 2010 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.
4
5 #include "chrome/browser/sync/glue/password_model_associator.h"
6
7 #include <set>
8
9 #include "base/stl_util-inl.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/password_manager/password_store.h"
12 #include "chrome/browser/profile.h"
13 #include "chrome/browser/sync/engine/syncapi.h"
14 #include "chrome/browser/sync/profile_sync_service.h"
15 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
16 #include "net/base/escape.h"
17 #include "webkit/glue/password_form.h"
18
19 namespace browser_sync {
20
21 const char kPasswordTag[] = "google_chrome_passwords";
22
23 PasswordModelAssociator::PasswordModelAssociator(
24 ProfileSyncService* sync_service,
25 PasswordStore* password_store,
26 UnrecoverableErrorHandler* error_handler)
27 : sync_service_(sync_service),
28 password_store_(password_store),
29 error_handler_(error_handler),
30 password_node_id_(sync_api::kInvalidId),
31 abort_association_pending_(false),
32 expected_loop_(MessageLoop::current()) {
33 DCHECK(sync_service_);
34 DCHECK(password_store_);
35 DCHECK(error_handler_);
36 #if defined(OS_MACOSX)
37 DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::UI));
38 #else
39 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
40 #endif
41 }
42
43 bool PasswordModelAssociator::AssociateModels() {
44 DCHECK(expected_loop_ == MessageLoop::current());
45 {
46 AutoLock lock(abort_association_pending_lock_);
47 abort_association_pending_ = false;
48 }
49
50 sync_api::WriteTransaction trans(
51 sync_service_->backend()->GetUserShareHandle());
52 sync_api::ReadNode password_root(&trans);
53 if (!password_root.InitByTagLookup(kPasswordTag)) {
54 LOG(ERROR) << "Server did not create the top-level password node. We "
55 << "might be running against an out-of-date server.";
56 return false;
57 }
58
59 std::vector<webkit_glue::PasswordForm*> passwords;
60 if (!password_store_->FillAutofillableLogins(&passwords) ||
61 !password_store_->FillBlacklistLogins(&passwords)) {
62 STLDeleteElements(&passwords);
63 LOG(ERROR) << "Could not get the password entries.";
64 return false;
65 }
66
67 std::set<std::string> current_passwords;
68 PasswordVector new_passwords;
69 PasswordVector updated_passwords;
70
71 for (std::vector<webkit_glue::PasswordForm*>::iterator ix = passwords.begin();
72 ix != passwords.end(); ++ix) {
73 if (IsAbortPending())
74 return false;
75 std::string tag = MakeTag(**ix);
76
77 sync_api::ReadNode node(&trans);
78 if (node.InitByClientTagLookup(syncable::PASSWORD, tag)) {
79 sync_pb::PasswordSpecificsData password;
80 if (!node.GetPasswordSpecifics(&password)) {
81 STLDeleteElements(&passwords);
82 LOG(ERROR) << "Failed to get password specifics from sync node.";
83 return false;
84 }
85 DCHECK_EQ(tag, MakeTag(password));
86
87 webkit_glue::PasswordForm new_password;
88
89 if (MergePasswords(password, **ix, &new_password)) {
90 sync_api::WriteNode write_node(&trans);
91 if (!write_node.InitByClientTagLookup(syncable::PASSWORD, tag)) {
92 STLDeleteElements(&passwords);
93 LOG(ERROR) << "Failed to edit password sync node.";
94 return false;
95 }
96 WriteToSyncNode(new_password, &write_node);
97 updated_passwords.push_back(new_password);
98 }
99
100 Associate(&tag, node.GetId());
101 } else {
102 sync_api::WriteNode node(&trans);
103 if (!node.InitUniqueByCreation(syncable::PASSWORD,
104 password_root, tag)) {
105 STLDeleteElements(&passwords);
106 LOG(ERROR) << "Failed to create password sync node.";
107 return false;
108 }
109
110 WriteToSyncNode(**ix, &node);
111
112 Associate(&tag, node.GetId());
113 }
114
115 current_passwords.insert(tag);
116 }
117
118 STLDeleteElements(&passwords);
119
120 int64 sync_child_id = password_root.GetFirstChildId();
121 while (sync_child_id != sync_api::kInvalidId) {
122 sync_api::ReadNode sync_child_node(&trans);
123 if (!sync_child_node.InitByIdLookup(sync_child_id)) {
124 LOG(ERROR) << "Failed to fetch child node.";
125 return false;
126 }
127 sync_pb::PasswordSpecificsData password;
128 if (!sync_child_node.GetPasswordSpecifics(&password)) {
129 LOG(ERROR) << "Failed to get specifics from password node.";
130 return false;
131 }
132 std::string tag = MakeTag(password);
133
134 // The password only exists on the server. Add it to the local
135 // model.
136 if (current_passwords.find(tag) == current_passwords.end()) {
137 webkit_glue::PasswordForm new_password;
138
139 CopyPassword(password, &new_password);
140 Associate(&tag, sync_child_node.GetId());
141 new_passwords.push_back(new_password);
142 }
143
144 sync_child_id = sync_child_node.GetSuccessorId();
145 }
146
147 if (!WriteToPasswordStore(&new_passwords, &updated_passwords, NULL)) {
148 LOG(ERROR) << "Failed to write passwords.";
149 return false;
150 }
151
152 return true;
153 }
154
155 bool PasswordModelAssociator::DeleteAllNodes(
156 sync_api::WriteTransaction* trans) {
157 DCHECK(expected_loop_ == MessageLoop::current());
158 for (PasswordToSyncIdMap::iterator node_id = id_map_.begin();
159 node_id != id_map_.end(); ++node_id) {
160 sync_api::WriteNode sync_node(trans);
161 if (!sync_node.InitByIdLookup(node_id->second)) {
162 LOG(ERROR) << "Typed url node lookup failed.";
163 return false;
164 }
165 sync_node.Remove();
166 }
167
168 id_map_.clear();
169 id_map_inverse_.clear();
170 return true;
171 }
172
173 bool PasswordModelAssociator::DisassociateModels() {
174 id_map_.clear();
175 id_map_inverse_.clear();
176 return true;
177 }
178
179 bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
180 DCHECK(has_nodes);
181 *has_nodes = false;
182 int64 password_sync_id;
183 if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) {
184 LOG(ERROR) << "Server did not create the top-level password node. We "
185 << "might be running against an out-of-date server.";
186 return false;
187 }
188 sync_api::ReadTransaction trans(
189 sync_service_->backend()->GetUserShareHandle());
190
191 sync_api::ReadNode password_node(&trans);
192 if (!password_node.InitByIdLookup(password_sync_id)) {
193 LOG(ERROR) << "Server did not create the top-level password node. We "
194 << "might be running against an out-of-date server.";
195 return false;
196 }
197
198 // The sync model has user created nodes if the password folder has any
199 // children.
200 *has_nodes = sync_api::kInvalidId != password_node.GetFirstChildId();
201 return true;
202 }
203
204 bool PasswordModelAssociator::ChromeModelHasUserCreatedNodes(bool* has_nodes) {
205 DCHECK(has_nodes);
206 std::vector<webkit_glue::PasswordForm*> passwords;
207 if (!password_store_->FillAutofillableLogins(&passwords) ||
208 !password_store_->FillBlacklistLogins(&passwords)) {
209 STLDeleteElements(&passwords);
210 LOG(ERROR) << "Could not get the password entries.";
211 return false;
212 }
213
214 *has_nodes = !passwords.empty();
215 STLDeleteElements(&passwords);
216 return true;
217 }
218
219 void PasswordModelAssociator::AbortAssociation() {
220 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
221 AutoLock lock(abort_association_pending_lock_);
222 abort_association_pending_ = true;
223 }
224
225 bool PasswordModelAssociator::IsAbortPending() {
226 AutoLock lock(abort_association_pending_lock_);
227 return abort_association_pending_;
228 }
229
230 int64 PasswordModelAssociator::GetSyncIdFromChromeId(
231 const std::string password) {
232 PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
233 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
234 }
235
236 void PasswordModelAssociator::Associate(
237 const std::string* password, int64 sync_id) {
238 DCHECK(expected_loop_ == MessageLoop::current());
239 DCHECK_NE(sync_api::kInvalidId, sync_id);
240 DCHECK(id_map_.find(*password) == id_map_.end());
241 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
242 id_map_[*password] = sync_id;
243 id_map_inverse_[sync_id] = *password;
244 }
245
246 void PasswordModelAssociator::Disassociate(int64 sync_id) {
247 DCHECK(expected_loop_ == MessageLoop::current());
248 SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id);
249 if (iter == id_map_inverse_.end())
250 return;
251 CHECK(id_map_.erase(iter->second));
252 id_map_inverse_.erase(iter);
253 }
254
255 bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
256 int64* sync_id) {
257 sync_api::ReadTransaction trans(
258 sync_service_->backend()->GetUserShareHandle());
259 sync_api::ReadNode sync_node(&trans);
260 if (!sync_node.InitByTagLookup(tag.c_str()))
261 return false;
262 *sync_id = sync_node.GetId();
263 return true;
264 }
265
266 bool PasswordModelAssociator::WriteToPasswordStore(
267 const PasswordVector* new_passwords,
268 const PasswordVector* updated_passwords,
269 const PasswordVector* deleted_passwords) {
270 if (new_passwords) {
271 for (PasswordVector::const_iterator password = new_passwords->begin();
272 password != new_passwords->end(); ++password) {
273 password_store_->AddLoginImpl(*password);
274 }
275 }
276
277 if (updated_passwords) {
278 for (PasswordVector::const_iterator password = updated_passwords->begin();
279 password != updated_passwords->end(); ++password) {
280 password_store_->UpdateLoginImpl(*password);
281 }
282 }
283
284 if (deleted_passwords) {
285 for (PasswordVector::const_iterator password = deleted_passwords->begin();
286 password != deleted_passwords->end(); ++password) {
287 password_store_->RemoveLoginImpl(*password);
288 }
289 }
290 return true;
291 }
292
293 // static
294 void PasswordModelAssociator::CopyPassword(
295 const sync_pb::PasswordSpecificsData& password,
296 webkit_glue::PasswordForm* new_password) {
297 new_password->scheme =
298 static_cast<webkit_glue::PasswordForm::Scheme>(password.scheme());
299 new_password->signon_realm = password.signon_realm();
300 new_password->origin = GURL(password.origin());
301 new_password->action = GURL(password.action());
302 new_password->username_element =
303 UTF8ToUTF16(password.username_element());
304 new_password->password_element =
305 UTF8ToUTF16(password.password_element());
306 new_password->username_value =
307 UTF8ToUTF16(password.username_value());
308 new_password->password_value =
309 UTF8ToUTF16(password.password_value());
310 new_password->ssl_valid = password.ssl_valid();
311 new_password->preferred = password.preferred();
312 new_password->date_created =
313 base::Time::FromInternalValue(password.date_created());
314 new_password->blacklisted_by_user =
315 password.blacklisted();
316 }
317
318 // static
319 bool PasswordModelAssociator::MergePasswords(
320 const sync_pb::PasswordSpecificsData& password,
321 const webkit_glue::PasswordForm& password_form,
322 webkit_glue::PasswordForm* new_password) {
323 DCHECK(new_password);
324
325 if (password.scheme() == password_form.scheme &&
326 password_form.signon_realm == password.signon_realm() &&
327 password_form.origin.spec() == password.origin() &&
328 password_form.action.spec() == password.action() &&
329 UTF16ToUTF8(password_form.username_element) ==
330 password.username_element() &&
331 UTF16ToUTF8(password_form.password_element) ==
332 password.password_element() &&
333 UTF16ToUTF8(password_form.username_value) ==
334 password.username_value() &&
335 UTF16ToUTF8(password_form.password_value) ==
336 password.password_value() &&
337 password.ssl_valid() == password_form.ssl_valid &&
338 password.preferred() == password_form.preferred &&
339 password.date_created() == password_form.date_created.ToInternalValue() &&
340 password.blacklisted() == password_form.blacklisted_by_user) {
341 return false;
342 }
343
344 // If the passwords differ, we take the one that was created more recently.
345 if (base::Time::FromInternalValue(password.date_created()) <=
346 password_form.date_created) {
347 *new_password = password_form;
348 } else {
349 CopyPassword(password, new_password);
350 }
351
352 return true;
353 }
354
355 // static
356 void PasswordModelAssociator::WriteToSyncNode(
357 const webkit_glue::PasswordForm& password_form,
358 sync_api::WriteNode* node) {
359 sync_pb::PasswordSpecificsData password;
360 password.set_scheme(password_form.scheme);
361 password.set_signon_realm(password_form.signon_realm);
362 password.set_origin(password_form.origin.spec());
363 password.set_action(password_form.action.spec());
364 password.set_username_element(UTF16ToUTF8(password_form.username_element));
365 password.set_password_element(UTF16ToUTF8(password_form.password_element));
366 password.set_username_value(UTF16ToUTF8(password_form.username_value));
367 password.set_password_value(UTF16ToUTF8(password_form.password_value));
368 password.set_ssl_valid(password_form.ssl_valid);
369 password.set_preferred(password_form.preferred);
370 password.set_date_created(password_form.date_created.ToInternalValue());
371 password.set_blacklisted(password_form.blacklisted_by_user);
372
373 node->SetPasswordSpecifics(password);
374 }
375
376 // static
377 std::string PasswordModelAssociator::MakeTag(
378 const webkit_glue::PasswordForm& password) {
379 return MakeTag(password.signon_realm,
380 password.origin.spec(),
381 password.action.spec());
382 }
383
384 // static
385 std::string PasswordModelAssociator::MakeTag(
386 const sync_pb::PasswordSpecificsData& password) {
387 return MakeTag(password.signon_realm(),
388 password.origin(),
389 password.action());
390 }
391
392 // static
393 std::string PasswordModelAssociator::MakeTag(
394 const std::string& signon_realm,
395 const std::string& origin,
396 const std::string& action) {
397 return EscapePath(signon_realm) + "|" +
398 EscapePath(origin) + "|" +
399 EscapePath(action);
400 }
401
402 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/glue/password_model_associator.h ('k') | chrome/browser/sync/glue/password_model_worker.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698