1 // Copyright (c) 2011 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/sync/engine/syncapi.h" 13 #include "chrome/browser/sync/profile_sync_service.h" 14 #include "chrome/browser/sync/protocol/password_specifics.pb.h" 15 #include "net/base/escape.h" 16 #include "webkit/glue/password_form.h" 17 18 namespace browser_sync { 19 20 const char kPasswordTag[] = "google_chrome_passwords"; 21 22 PasswordModelAssociator::PasswordModelAssociator( 23 ProfileSyncService* sync_service, 24 PasswordStore* password_store) 25 : sync_service_(sync_service), 26 password_store_(password_store), 27 password_node_id_(sync_api::kInvalidId), 28 abort_association_pending_(false), 29 expected_loop_(MessageLoop::current()) { 30 DCHECK(sync_service_); 31 DCHECK(password_store_); 32 #if defined(OS_MACOSX) 33 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 34 #else 35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 36 #endif 37 } 38 39 PasswordModelAssociator::~PasswordModelAssociator() {} 40 41 bool PasswordModelAssociator::AssociateModels() { 42 DCHECK(expected_loop_ == MessageLoop::current()); 43 { 44 base::AutoLock lock(abort_association_pending_lock_); 45 abort_association_pending_ = false; 46 } 47 48 // We must not be holding a transaction when we interact with the password 49 // store, as it can post tasks to the UI thread which can itself be blocked 50 // on our transaction, resulting in deadlock. (http://crbug.com/70658) 51 std::vector<webkit_glue::PasswordForm*> passwords; 52 if (!password_store_->FillAutofillableLogins(&passwords) || 53 !password_store_->FillBlacklistLogins(&passwords)) { 54 STLDeleteElements(&passwords); 55 LOG(ERROR) << "Could not get the password entries."; 56 return false; 57 } 58 59 std::set<std::string> current_passwords; 60 PasswordVector new_passwords; 61 PasswordVector updated_passwords; 62 { 63 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); 64 sync_api::ReadNode password_root(&trans); 65 if (!password_root.InitByTagLookup(kPasswordTag)) { 66 LOG(ERROR) << "Server did not create the top-level password node. We " 67 << "might be running against an out-of-date server."; 68 return false; 69 } 70 71 for (std::vector<webkit_glue::PasswordForm*>::iterator ix = 72 passwords.begin(); 73 ix != passwords.end(); ++ix) { 74 if (IsAbortPending()) 75 return false; 76 std::string tag = MakeTag(**ix); 77 78 sync_api::ReadNode node(&trans); 79 if (node.InitByClientTagLookup(syncable::PASSWORDS, tag)) { 80 const sync_pb::PasswordSpecificsData& password = 81 node.GetPasswordSpecifics(); 82 DCHECK_EQ(tag, MakeTag(password)); 83 84 webkit_glue::PasswordForm new_password; 85 86 if (MergePasswords(password, **ix, &new_password)) { 87 sync_api::WriteNode write_node(&trans); 88 if (!write_node.InitByClientTagLookup(syncable::PASSWORDS, tag)) { 89 STLDeleteElements(&passwords); 90 LOG(ERROR) << "Failed to edit password sync node."; 91 return false; 92 } 93 WriteToSyncNode(new_password, &write_node); 94 updated_passwords.push_back(new_password); 95 } 96 97 Associate(&tag, node.GetId()); 98 } else { 99 sync_api::WriteNode node(&trans); 100 if (!node.InitUniqueByCreation(syncable::PASSWORDS, 101 password_root, tag)) { 102 STLDeleteElements(&passwords); 103 LOG(ERROR) << "Failed to create password sync node."; 104 return false; 105 } 106 107 WriteToSyncNode(**ix, &node); 108 109 Associate(&tag, node.GetId()); 110 } 111 112 current_passwords.insert(tag); 113 } 114 115 STLDeleteElements(&passwords); 116 117 int64 sync_child_id = password_root.GetFirstChildId(); 118 while (sync_child_id != sync_api::kInvalidId) { 119 sync_api::ReadNode sync_child_node(&trans); 120 if (!sync_child_node.InitByIdLookup(sync_child_id)) { 121 LOG(ERROR) << "Failed to fetch child node."; 122 return false; 123 } 124 const sync_pb::PasswordSpecificsData& password = 125 sync_child_node.GetPasswordSpecifics(); 126 std::string tag = MakeTag(password); 127 128 // The password only exists on the server. Add it to the local 129 // model. 130 if (current_passwords.find(tag) == current_passwords.end()) { 131 webkit_glue::PasswordForm new_password; 132 133 CopyPassword(password, &new_password); 134 Associate(&tag, sync_child_node.GetId()); 135 new_passwords.push_back(new_password); 136 } 137 138 sync_child_id = sync_child_node.GetSuccessorId(); 139 } 140 } 141 142 // We must not be holding a transaction when we interact with the password 143 // store, as it can post tasks to the UI thread which can itself be blocked 144 // on our transaction, resulting in deadlock. (http://crbug.com/70658) 145 if (!WriteToPasswordStore(&new_passwords, &updated_passwords, NULL)) { 146 LOG(ERROR) << "Failed to write passwords."; 147 return false; 148 } 149 150 return true; 151 } 152 153 bool PasswordModelAssociator::DeleteAllNodes( 154 sync_api::WriteTransaction* trans) { 155 DCHECK(expected_loop_ == MessageLoop::current()); 156 for (PasswordToSyncIdMap::iterator node_id = id_map_.begin(); 157 node_id != id_map_.end(); ++node_id) { 158 sync_api::WriteNode sync_node(trans); 159 if (!sync_node.InitByIdLookup(node_id->second)) { 160 LOG(ERROR) << "Typed url node lookup failed."; 161 return false; 162 } 163 sync_node.Remove(); 164 } 165 166 id_map_.clear(); 167 id_map_inverse_.clear(); 168 return true; 169 } 170 171 bool PasswordModelAssociator::DisassociateModels() { 172 id_map_.clear(); 173 id_map_inverse_.clear(); 174 return true; 175 } 176 177 bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 178 DCHECK(has_nodes); 179 *has_nodes = false; 180 int64 password_sync_id; 181 if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) { 182 LOG(ERROR) << "Server did not create the top-level password node. We " 183 << "might be running against an out-of-date server."; 184 return false; 185 } 186 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); 187 188 sync_api::ReadNode password_node(&trans); 189 if (!password_node.InitByIdLookup(password_sync_id)) { 190 LOG(ERROR) << "Server did not create the top-level password node. We " 191 << "might be running against an out-of-date server."; 192 return false; 193 } 194 195 // The sync model has user created nodes if the password folder has any 196 // children. 197 *has_nodes = sync_api::kInvalidId != password_node.GetFirstChildId(); 198 return true; 199 } 200 201 void PasswordModelAssociator::AbortAssociation() { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 base::AutoLock lock(abort_association_pending_lock_); 204 abort_association_pending_ = true; 205 } 206 207 bool PasswordModelAssociator::CryptoReadyIfNecessary() { 208 // We only access the cryptographer while holding a transaction. 209 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); 210 // We always encrypt passwords, so no need to check if encryption is enabled. 211 return sync_service_->IsCryptographerReady(&trans); 212 } 213 214 const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId( 215 int64 sync_id) { 216 return NULL; 217 } 218 219 bool PasswordModelAssociator::InitSyncNodeFromChromeId( 220 const std::string& node_id, 221 sync_api::BaseNode* sync_node) { 222 return false; 223 } 224 225 bool PasswordModelAssociator::IsAbortPending() { 226 base::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(sync_service_->GetUserShare()); 258 sync_api::ReadNode sync_node(&trans); 259 if (!sync_node.InitByTagLookup(tag.c_str())) 260 return false; 261 *sync_id = sync_node.GetId(); 262 return true; 263 } 264 265 bool PasswordModelAssociator::WriteToPasswordStore( 266 const PasswordVector* new_passwords, 267 const PasswordVector* updated_passwords, 268 const PasswordVector* deleted_passwords) { 269 if (new_passwords) { 270 for (PasswordVector::const_iterator password = new_passwords->begin(); 271 password != new_passwords->end(); ++password) { 272 password_store_->AddLoginImpl(*password); 273 } 274 } 275 276 if (updated_passwords) { 277 for (PasswordVector::const_iterator password = updated_passwords->begin(); 278 password != updated_passwords->end(); ++password) { 279 password_store_->UpdateLoginImpl(*password); 280 } 281 } 282 283 if (deleted_passwords) { 284 for (PasswordVector::const_iterator password = deleted_passwords->begin(); 285 password != deleted_passwords->end(); ++password) { 286 password_store_->RemoveLoginImpl(*password); 287 } 288 } 289 290 if (new_passwords || updated_passwords || deleted_passwords) { 291 // We have to notify password store observers of the change by hand since 292 // we use internal password store interfaces to make changes synchronously. 293 password_store_->PostNotifyLoginsChanged(); 294 } 295 return true; 296 } 297 298 // static 299 void PasswordModelAssociator::CopyPassword( 300 const sync_pb::PasswordSpecificsData& password, 301 webkit_glue::PasswordForm* new_password) { 302 new_password->scheme = 303 static_cast<webkit_glue::PasswordForm::Scheme>(password.scheme()); 304 new_password->signon_realm = password.signon_realm(); 305 new_password->origin = GURL(password.origin()); 306 new_password->action = GURL(password.action()); 307 new_password->username_element = 308 UTF8ToUTF16(password.username_element()); 309 new_password->password_element = 310 UTF8ToUTF16(password.password_element()); 311 new_password->username_value = 312 UTF8ToUTF16(password.username_value()); 313 new_password->password_value = 314 UTF8ToUTF16(password.password_value()); 315 new_password->ssl_valid = password.ssl_valid(); 316 new_password->preferred = password.preferred(); 317 new_password->date_created = 318 base::Time::FromInternalValue(password.date_created()); 319 new_password->blacklisted_by_user = 320 password.blacklisted(); 321 } 322 323 // static 324 bool PasswordModelAssociator::MergePasswords( 325 const sync_pb::PasswordSpecificsData& password, 326 const webkit_glue::PasswordForm& password_form, 327 webkit_glue::PasswordForm* new_password) { 328 DCHECK(new_password); 329 330 if (password.scheme() == password_form.scheme && 331 password_form.signon_realm == password.signon_realm() && 332 password_form.origin.spec() == password.origin() && 333 password_form.action.spec() == password.action() && 334 UTF16ToUTF8(password_form.username_element) == 335 password.username_element() && 336 UTF16ToUTF8(password_form.password_element) == 337 password.password_element() && 338 UTF16ToUTF8(password_form.username_value) == 339 password.username_value() && 340 UTF16ToUTF8(password_form.password_value) == 341 password.password_value() && 342 password.ssl_valid() == password_form.ssl_valid && 343 password.preferred() == password_form.preferred && 344 password.date_created() == password_form.date_created.ToInternalValue() && 345 password.blacklisted() == password_form.blacklisted_by_user) { 346 return false; 347 } 348 349 // If the passwords differ, we take the one that was created more recently. 350 if (base::Time::FromInternalValue(password.date_created()) <= 351 password_form.date_created) { 352 *new_password = password_form; 353 } else { 354 CopyPassword(password, new_password); 355 } 356 357 return true; 358 } 359 360 // static 361 void PasswordModelAssociator::WriteToSyncNode( 362 const webkit_glue::PasswordForm& password_form, 363 sync_api::WriteNode* node) { 364 sync_pb::PasswordSpecificsData password; 365 password.set_scheme(password_form.scheme); 366 password.set_signon_realm(password_form.signon_realm); 367 password.set_origin(password_form.origin.spec()); 368 password.set_action(password_form.action.spec()); 369 password.set_username_element(UTF16ToUTF8(password_form.username_element)); 370 password.set_password_element(UTF16ToUTF8(password_form.password_element)); 371 password.set_username_value(UTF16ToUTF8(password_form.username_value)); 372 password.set_password_value(UTF16ToUTF8(password_form.password_value)); 373 password.set_ssl_valid(password_form.ssl_valid); 374 password.set_preferred(password_form.preferred); 375 password.set_date_created(password_form.date_created.ToInternalValue()); 376 password.set_blacklisted(password_form.blacklisted_by_user); 377 378 node->SetPasswordSpecifics(password); 379 } 380 381 // static 382 std::string PasswordModelAssociator::MakeTag( 383 const webkit_glue::PasswordForm& password) { 384 return MakeTag(password.origin.spec(), 385 UTF16ToUTF8(password.username_element), 386 UTF16ToUTF8(password.username_value), 387 UTF16ToUTF8(password.password_element), 388 password.signon_realm); 389 } 390 391 // static 392 std::string PasswordModelAssociator::MakeTag( 393 const sync_pb::PasswordSpecificsData& password) { 394 return MakeTag(password.origin(), 395 password.username_element(), 396 password.username_value(), 397 password.password_element(), 398 password.signon_realm()); 399 } 400 401 // static 402 std::string PasswordModelAssociator::MakeTag( 403 const std::string& origin_url, 404 const std::string& username_element, 405 const std::string& username_value, 406 const std::string& password_element, 407 const std::string& signon_realm) { 408 return EscapePath(origin_url) + "|" + 409 EscapePath(username_element) + "|" + 410 EscapePath(username_value) + "|" + 411 EscapePath(password_element) + "|" + 412 EscapePath(signon_realm); 413 } 414 415 } // namespace browser_sync 416