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